Share
Embed
Ruby 3の新機能としての静的型検査の開発
PPL 2021 https://jssst-ppl.org/workshop/2021/
Soutaro Matsumoto
March 10, 2021
Transcript
41 slides
-
দຊफଠ w !TPVUBSP w 3VCZίϛολ ʙ 3VCZͷܕͷࣄ w 4RVBSFגࣜձࣾ
ʙ ࣾͷ3VCZϓϩάϥϜʹܕΛ͚Δࣄ w ത࢜ʢֶʣஜେֶ 3VCZϓϩάϥϜͷܕΛਪ -
# The conference object represents a conference. # # conf
= Conference.new("RubyConf 2020") # conf.talks << talk1 # conf.talks << talk2 # class Conference # The name of the conference. attr_reader name: String # Talks of the conference. attr_reader talks: Array[Talk] def initialize: (String name) -> void # Yields all speakers of the conference. # Deduped, and no order guaranteed. def each_speaker: { (Speaker) -> void } -> void | () -> Enumerator[Speaker, void] end -
Union types Generics untyped type String | Symbol Integer? #==
Integer | nil Hash[String, Array[Integer]] Array[String | Integer] def eval: (String) -> untyped Tuple & record [Integer, String] # ݻఆͷྻ { id: Integer, name: String } # Ωʔݻఆͷϋογϡ Class/module types Object, Kernel singleton(String), singleton(Array) -
class Server < ApplicationRecord validates :name, :length => { :in
=> 3..30 } validates :uri, :length => { :in => 20..200 } validates :ordinal, :numericality => true, :uniqueness => true end class Server < ApplicationRecord attr_accessor name (): String attr_accessor uri (): String def name_changed: () -> bool def name_change: () -> [String?, String?] def name_will_change: () -> void def name_was: () -> String? def name_previously_changed?: () -> bool def name_previously_was: () -> String? def name_before_last_save: () -> String? def uri_changed: () -> bool def uri_change: () -> [String?, String?] -
ྺ࢙ w શ෦ܕΛਪ͠Α͏ͱ͢Δάϧʔϓ w ར༻ऀ͕ॻ͍ͨϓϩάϥϜʹܕऍ͕͋Δ͜ͱΛԾఆ͢Δάϧʔϓ
.-ͷܕਪʹجͮ͘ͷʢদຊ ೆग़ʣ ϑϩʔղੳʹجͮ͘ͷʢদຊ ೆग़ʣ %JBNPOECBDL3VCZʢ'VSS 'PTUFSʣ 4UFFQʢদຊʣ 4PSCFU 4USJQF 5ZQF1SPGʢԕ౻ʣ NNDʢࡾӜʣ +VTUJO5JNF4UBUJD5ZQF$IFDLJOHGPS %ZOBNJD-BOHVBHFTʢ3FO 'PTUFSʣ -
3VCZʹ͚ͯ 4UFFQʢদຊʣ 4PSCFU 4USJQF 5ZQF1SPGʢԕ౻ʣ NNDʢࡾӜʣ
3VCZʹܕΛೖΕΔ͜ͱΛએݴ .BU[ 3VCZʹܕݕࠪثΛೖΕͳ͍͜ͱΛܾఆ .BU[ 3#4ͷಋೖΛܾఆ .BU[ 5ZQF1SPGͷಋೖΛܾఆ .BU[ ౦େֶిؾ௨৴ݚڀॴڞಉϓϩδΣΫτ SVCZUZQFTNFFUJOH -
4PSCFU w IUUQTTPSCFUPSH w 3VCZ%4-ͰܕΛॻ͘ class Conference extend T::Sig sig
do params(name: String).void end def initialize(name:) @name = name @speakers = T.let([], T::Array[Speaker]) end end -
4UFFQ w ϓϩάϥϜͷܕผͷఆٛϑΝΠϧʹॻ͘ w ίϝϯτͰΠϯϥΠϯܕऍ w ࣮ߦ࣌ͷґଘϥΠϒϥϦͳ͠ class Conference def
each_speaker(&block) # @type var speakers: Array[Speak speakers = [] talks.each do |talk| speakers << talk.speaker end speakers.each(&block) end end class Conference def each_speaker: { (Speaker) -> void } -> void end -
ͥΜͿߏత෦ܕͰߦͧ͘💪 w 4USJOHͷdϝιουΛશ෦࠶࣮ͨ͠ΫϥεΛ࡞Δ͜ͱඇݱ࣮త w 3VCZͷ࣮ߦ࣌ͷҙຯͱҰக͠ͳ͍ object.is_a?(String) case json when String
... when Numeric ... when Array ... end -
Ίͨ w ΠϯλϑΣʔεͷΈߏతʹ෦ܕΛఆ w ଞΫϥεͷܧঝؔʹΑͬͯఆ interface _Appendable def <<: (String)
-> void end class String def <<: (String) -> String end class Array[Elem] def <<: (Elem) -> Array[Elem] end class Integer def <<: (Integer) -> Integer end String <: _Appendable # => ✅ Array[String] <: _Appendable # => ✅ Integer <: _Appendable # => 💣 -
ࣗ༝ͳΦʔόʔϥΠυ w αϒΫϥε͕উखʹ͖ͳҙຯɾܕͰಉ໊ͷϝιουΛఆ͍ٛͯ͠Δ w 0CKFDUTFMFDUͱ"SSBZTFMFDU w 0CKFDUNFUIPEͱ)5513FRVFTUNFUIPE w .PEVMFDMBTT def
select: (Array[IO], Array[IO], Array[IO]) -> Array[Array[IO]] def select: () { (Elem) -> top } -> Array[Elem] | () -> Enumerator[Elem, Array[Elem]] 0CKFDUTFMFDU "SSBZTFMFDU -
ఘΊͨ w ΦʔόʔϥΠυͰϝιουͷܕ͕มΘΔͷɺϓϩάϥϚͷ w ϝιουͷܕʹΞϊςʔγϣϯΛ͚Δ㱺͕ଟ͗͢Δ w ࣮ࡍʹʹͳΒͳͦ͞͏ʢͲ͏ͤ3VCZͰ࣮ߦͰ͖ͳ͍͔Βʣ def get_method(object) #
Object#methodͩͱࢥͬͯݺͼग़͢ object.method(:to_s) end get_method("string") get_method(request) def get_method: (Object) -> void -
$PWBSJBOUͳίϨΫγϣϯ w "SSBZ<4USJOH>ͱ"SSBZ<0CKFDU>ͷؔ # @type var strings: Array[String] strings =
["foo", "bar"] # @type var objects: Array[Object] objects = strings # ͜ͷೖΛڐ͔͢ʁ objects.push(3) strings[2] # StringʁIntegerʁ -
ྑ͍ํ๏ͳ͍͔ʁ w +BWBͷΑ͏ʹΔͷͲ͏͔ʁ w ಛผͳ(FOFSJDTͷߏจͰɺҾʹܕม͕ग़ͯ͘ΔϝιουΛফ͢ def read_array: (Array[? extends Object])
-> void class Array[Elem] def []: (Integer) -> Elem # ͢ def <<: (Elem) -> Array[Elem] # ফ͢ end -
class Array[Elem] def find: (Elem) -> Elem? end class Array[Elem]
def find: (top) -> Elem? end -
class Array[Elem] def find: (Elem) -> Elem? end class Array[Elem]
def find: (top) -> Elem? end # ൺֱؔΛ֎͔Β͢ίϨΫγϣϯͩͱμϝͳͷͰ͋Μ·Γ͍ಓ͕ͳͦ͞͏ class Set[Elem] def initialize: { (Elem, Elem) -> Integer } -> void def find: (Elem) -> Elem? end -
ఘΊͨ w ྻͷཁૉ$PWBSJBOUͩͱܾΊͨ w ੈͷதͦ͏͍͏͜ͱʹͳ͍ͬͯΔʢ+BWB5ZQF4DSJQUͦ͏ʣ class Array[unchecked out Elem] def
<<: (Elem) -> Array[Elem] def pop: () -> Elem? # ͦͦpop͕μϝ end -
ܕఆٛͷςετ w ϥΠϒϥϦͷܕΛॻ͍ͯཉ͍͠ w ܕݕࠪثͳ͠ͰܕΛॻ͘ͷແཧ w 4UFFQΛ͏ͷͦΕͳΓʹେม w ΈࠐΈϥΠϒϥϦͷιʔείʔυ$ w
ॻ͍ͨܕͷਖ਼͠͞ΛͳΜͱͳ֬͘ೝ͢Δํ๏͕ඞཁ -
ܕఆٛͷςετ w ࣮ߦ࣌ݕࠪͱΈ߹ΘͤΔํ๏ʢΈࠐΈϥΠϒϥϦͰ༻ʣ w 4UFFQΛ͏ํ๏ʢHFN@SCT@DPMMFDUJPOͰ༻ʣ w ґଘ͕͋ΔͷͰɺςετ࣮ߦ͕ࠔ w ظ͞ΕΔܕΤϥʔΛอଘ͓͍ͯͯ͠ൺֱ #
io.read(0) assert_send_type "(Integer) -> String", io, :read, 0 $ steep check --with-expectations=test.yml -
ิީิͷੜ foo.ba← ͜͜ʂ foo.← ͜͜ʂ w ͜Εਖ਼͍͠3VCZίʔυ w ܕݕࠪͯ͠ɺΧʔιϧҐஔͷࣜͷܕ ͔ΒิީิΛੜ
w ͜ΕߏจΤϥʔ w Χʔιϧͷલ͕ͩͬͨΒɺҰจࣈ ফͯ͠Έͯɺߏจղੳ w ղੳͰ͖ͨΒͦͷܕใ͔Βิ w ҙ֎ͱྑ͍ײ͡Ͱಈ͘ -
ީิੜͷ্ w ίʔυ͕͍ʢߦͱ͔ʣͱܕݕ͕͍ࠪʢඪඵʣ 㱺ؔͳ͍෦Λফ͔ͯ͠Βܕݕࠪ͢Δ class Foo include Bar def
foo f # ͜͜Ͱิ end def bar ... end w 4UFFQͷܕݕࠪʹ࠷ଆͷEFG Ҏ֎ؔͳ͍ w ϝιουͷܕ3#4͔ΒಡΉ w Ωϟογϡͷঢ়گʹΑΔ͕ɺ࠷ ѱͰඵ͘Β͍ -
w ଟ૬Ϩίʔυܕʹجͮ͘3VCZϓϩάϥϜͷܕਪ দຊೆग़ w 3VCZϓϩάϥϜͷ੍ޚϑϩʔղੳͱͦͷ݈શੑͷূ໌ দຊೆग़ w 4UBUJD5ZQF*OGFSFODFGPS3VCZ
'VSS"O'PTUFS)JDLT w +VTUJO5JNF4UBUJD5ZQF$IFDLJOHGPS%ZOBNJD-BOHVBHFT 3FO'PTUFS w 1SPHSFTTSFQPSU3VCZʹ͓͚Δ੩తܕղੳͷ࣮ݱʹ͚ͯ ԕ౻দຊ্ॅҪদຊ w 4PSCFUIUUQTTPSCFUPSH w 4UFFQIUUQTHJUIVCDPNTPVUBSPTUFFQ w 3#4IUUQTHJUIVCDPNSVCZSCT w 5ZQF1SPGIUUQTHJUIVCDPNNBNFUZQFQSPG w HFN@SCT@DPMMFDUJPOIUUQTHJUIVCDPNSVCZHFN@SCT@DPMMFDUJPO