Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
型なし言語のための型
Search
Soutaro Matsumoto
November 03, 2019
Programming
7
13k
型なし言語のための型
富山Ruby会議01
Soutaro Matsumoto
November 03, 2019
Tweet
Share
More Decks by Soutaro Matsumoto
See All by Soutaro Matsumoto
API for docs
soutaro
4
1.9k
Rubyの標準添付ライブラリを開発する
soutaro
2
200
Embedding it into Ruby code
soutaro
4
23k
Parsing RBS
soutaro
0
1.7k
Ruby programming with types in action
soutaro
4
940
IDE Development with Ruby
soutaro
4
1.1k
Ruby 3の新機能としての静的型検査の開発
soutaro
4
7.4k
An Introduction to Static Typing in Ruby 3
soutaro
3
430
The State of Ruby 3 Typing
soutaro
0
710
Other Decks in Programming
See All in Programming
自作OSでDOOMを動かしてみた
zakki0925224
1
1.3k
コーディングは技術者(エンジニア)の嗜みでして / Learning the System Development Mindset from Rock Lady
mackey0225
2
310
PHPカンファレンス関西2025 基調講演
sugimotokei
6
1.1k
MCPで実現できる、Webサービス利用体験について
syumai
7
2.5k
Go製CLIツールをnpmで配布するには
syumai
2
1.1k
Amazon Q CLI開発で学んだAIコーディングツールの使い方
licux
3
180
[DevinMeetupTokyo2025] コード書かせないDevinの使い方
takumiyoshikawa
2
280
可変性を制する設計: 構造と振る舞いから考える概念モデリングとその実装
a_suenami
10
1.7k
Nuances on Kubernetes - RubyConf Taiwan 2025
envek
0
130
技術的負債で信頼性が限界だったWordPress運用をShifterで完全復活させた話
rvirus0817
0
1.2k
新世界の理解
koriym
0
130
Dart 参戦!!静的型付き言語界の隠れた実力者
kno3a87
0
180
Featured
See All Featured
Designing Experiences People Love
moore
142
24k
The World Runs on Bad Software
bkeepers
PRO
70
11k
Building Adaptive Systems
keathley
43
2.7k
Agile that works and the tools we love
rasmusluckow
329
21k
[RailsConf 2023] Rails as a piece of cake
palkan
56
5.8k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
229
22k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
Intergalactic Javascript Robots from Outer Space
tanoku
272
27k
The Language of Interfaces
destraynor
158
25k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
126
53k
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
The Power of CSS Pseudo Elements
geoffreycrofte
77
5.9k
Transcript
ܕͳ͠ݴޠͷͨΊͷܕ দຊफଠ !TPVUBSP
@soutaro
@soutaro
Ruby3ͷܕݕࠪ • RubyϓϩάϥϜͷࣜͷܕΛ࣮ߦͤͣʹఆ͠ɺ։ൃͷମݧΛվળ͢Δ • ύϑΥʔϚϯεతͰͳ͍ conf = Conference.find_by!(name: "ࢁRubyձٞ", volume:
1) conf.talks.each do |talk| puts talk.speaker.email end
Ruby3ͷܕݕࠪ • RubyϓϩάϥϜͷࣜͷܕΛ࣮ߦͤͣʹఆ͠ɺ։ൃͷମݧΛվળ͢Δ • ύϑΥʔϚϯεతͰͳ͍ conf = Conference.find_by!(name: "ࢁRubyձٞ", volume:
1) conf.talks.each do |talk| puts talk.speaker.email end ::Conference
Ruby3ͷܕݕࠪ • RubyϓϩάϥϜͷࣜͷܕΛ࣮ߦͤͣʹఆ͠ɺ։ൃͷମݧΛվળ͢Δ • ύϑΥʔϚϯεతͰͳ͍ conf = Conference.find_by!(name: "ࢁRubyձٞ", volume:
1) conf.talks.each do |talk| puts talk.speaker.email end ::Conference () { (::Talk) -> void } -> self
Ruby3ͷܕݕࠪ • RubyϓϩάϥϜͷࣜͷܕΛ࣮ߦͤͣʹఆ͠ɺ։ൃͷମݧΛվળ͢Δ • ύϑΥʔϚϯεతͰͳ͍ conf = Conference.find_by!(name: "ࢁRubyձٞ", volume:
1) conf.talks.each do |talk| puts talk.speaker.email end ::String ::Conference () { (::Talk) -> void } -> self
૬ؔਤ type-profiler Steep Sorbet RDL RBS (stdlib types) Level 1
ܕݕࠪ Level 2 ܕݕࠪ Type signature language
૬ؔਤ type-profiler Steep Sorbet RDL RBS (stdlib types) Level 1
ܕݕࠪ Level 2 ܕݕࠪ Type signature language ༻ ༻
૬ؔਤ type-profiler Steep Sorbet RDL RBS (stdlib types) Level 1
ܕݕࠪ Level 2 ܕݕࠪ Type signature language ༻ ༻ Ruby3ʹಉࠝ ͖ͳͷΛΠϯετʔϧ
3#4SVCZTJHOBUVSF w 3#4ΛಡΜͰɺΫϥεఆٛͷใΛॲཧ͢ΔϥΠϒϥϦ w ৄ͘͠དྷिͷ3VCZ)BDL$IBMMFOHF)PMJEBZͰ IUUQTSIDDPOOQBTTDPNFWFOU class Set[A] def initialize:
(_Each[A, untyped]) -> void | () -> void def intersection: (_Each[A, untyped]) -> self ... end
ΞτϥΠϯ w 3VCZͷ w ܕͳ͠ݴޠͷͨΊͷܕ w 5ZQF4DSJQUͱ࠷ۙͷྲྀΕ w 3VCZͷͨΊͷܕ
ܕͳ͠ݴޠͷͨΊͷܕ w ܕ w ίϯύΠϧ࣌ʹ໌͢Δࣜͷଐੑ w ͋ΔछͷޡΓΛݕग़Ͱ͖Δʢܕݕࠪʣ w ܕͳ͠ݴޠ w
ίϯύΠϧ࣌ʹܕݕࠪ͞Εͳ͍ݴޠʢ㲈ಈతܕ͚ݴޠʣ w 3VCZͱ͔
ίʔυฤू ίϯύΠϧ ࣮ߦ ܕݕࠪ ίʔυฤू ίϯύΠϧ ࣮ߦ ܕݕࠪ
class Person def name @name end def name=(value) @name =
value end end if rand(10) < 5 person = Person.new person.nme = "Soutaro Matsumoto" # NoMethodError end w ܕݕࠪʹΑͬͯɺཏతʹʢJGͷதʣݕࠪ͞Εͯɺ࣮ ߦલʹ͕ݕग़͞ΕΔʢίϯύΠϧ࣌ʣ w ܕݕࠪ͠ͳ͍߹ʹɺϓϩάϥϜͷ࣮ߦঢ়گʹΑͬ ͯɺ͕ݟ͔ͭΒͳ͍ʢ࣮ߦ࣌ʣ
ܕݕࠪͷಘࣦ w ͕ࣄલʹݕग़Ͱ͖Δ w ཏతͳݕࠪʢ࣮ߦͷύεʹґଘ͠ͳ͍ʣ w ॻ͚ΔϓϩάϥϜʹ੍ݶ͕ՃΘΔ w ࣮ࡍʹ࣮ߦ͞Εͳ͍ύεʹ͍ͭͯݕࠪ͞Εͯ͠·͏
4DIFNF 4.- )BTLFMM +BWB 3VCZ +BWB4DSJQU 1)1 1ZUIPO 5ZQF4DSJQU 4XJGU
4DIFNF 4.- )BTLFMM +BWB 3VCZ +BWB4DSJQU 1)1 1ZUIPO 5ZQF4DSJQU 4XJGU
ܕਪ 4PGU5ZQJOH (FOFSJDT '+ (SBEVBM5ZQJOH
4DIFNF 4.- )BTLFMM +BWB 3VCZ +BWB4DSJQU 1)1 1ZUIPO 5ZQF4DSJQU 4XJGU
ܕਪ 4PGU5ZQJOH (FOFSJDT '+ (SBEVBM5ZQJOH
4DIFNF 4.- )BTLFMM +BWB 3VCZ +BWB4DSJQU 1)1 1ZUIPO 5ZQF4DSJQU 4XJGU
ܕਪ 4PGU5ZQJOH (FOFSJDT '+ (SBEVBM5ZQJOH
.-ͷܕਪ w ʮ࠙ஸೡʹશ෦ͷࣜʹܕΛॻ͔ͳͯ͘ྑ͍ʯͱ͍͏ൃݟ w ύϥϝʔλଟ૬ʢେମ(FOFSJDTͷ͜ͱʣ # let rec map f
xs = match xs with [] -> [] | x::xs -> f x :: map f xs;; val map : ('a -> 'b) -> 'a list -> 'b list = <fun>
4PGU5ZQJOH w ܕͳ͠ͷݴޠʹܕΛ͚͍ͨ w ܕ͕͍ͭͨ෦Τϥʔ͕ݕग़Ͱ͖Δ w Θ͔Βͳ͍ͷ࣮ߦ࣌ʹݕࠪ͢Δ w ಈ͘ϓϩάϥϜͰ͖Δ͚ͩͦͷ··ܕݕࠪΛ௨͍ͨ͠ w
ܕऍઈରॻ͖ͨ͘ͳ͍
6OJPOUZQFT def f(x) if x 1 else "2" end end
6OJPOUZQFT def f(x) if x 1 else "2" end end
(bool) -> (Integer | String)
'MPXTFOTJUJWF5ZQJOH def g(n) n.abs # NoMethodError n.bytes # n: Integer
| String case n when Integer n.abs # n: Integer when String n.bytes # n: String end end (Integer | String) -> Integer
4PGU5ZQJOH w 4DIFNFϓϩάϥϜͷܕ͚ʹඞཁͳͷ͕Θ͔ͬͨ w 6OJPOUZQFT 'MPXTFOTJUJWFUZQJOH w ࣮ w
.-ͷܕਪʹجͮ͘ͷ w ϑϩʔղੳ͢Δͷ
ͬͱۙతͳݴޠͰʁ w +BWB4DSJQUͱ͔3VCZͱ͔< > w ΦϒδΣΫτʹର͢Δଟ૬ੑ BLBμοΫλΠϐϯά w ʮ͋Δϝιου͕ఆٛ͞Ε͍ͯΔҙͷΫϥεʯΈ͍ͨͳܕ
def add(x, y) x + y end def add: [A, B] ({ +: (A) -> B }, A) -> B
3VCZͰࢼͨ͠ͱ͜Ζ w ͬͺΓݫ͍͠ w .-ͷܕਪͰॲཧͰ͖ͳ͍ఆ͕ٛͰͯ͘Δ "SSBZNBQ w Ϋϥεఆ໋͕ٛྩతʹมΘ͍ͬͯ͘ͷ͕ݫ͍͠ʢϝλϓϩάϥϛϯάʣ w
ͦͦյΕ͍ͯΔఆ͕ٛͨ͘͞Μ͋Δ w "SSBZTFMFDUWT0CKFDUTFMFDU w )BTIΛฦ͢IBTIΛఆٛͨ͜͠ͱ͕ͳ͍ਓ
"SSBZJTB0CKFDU w 0CKFDUʹରͯ͠ಈ͘ϓϩάϥϜ"SSBZʹରͯ͠ಈ͘ʁʁ class Object def use_select select [STDIN], [],
[] end end "".use_select # OK [1,2,3].use_select # ????
4PGU5ZQJOH͔ΒಘΒΕͨݟ w จͰޭ͍ͯ͠ΔΑ͏ʹݟ͑Δ͕࣮༻Ϩϕϧͷͷͳ͍ w ඞཁͦ͏ͳܕγεςϜͷػೳΘ͔͖ͬͯͨ w Ή͠ΖܕΛॻ͍ͨํ͕ྑ͍ͷͰʁʁ w ϓϩάϥϚͷҙਤΛܕͰදݱ͢Δ w
ҙਤͱҰக͠ͳ͍ίʔυΛݕग़͢Δ
(SBEVBM5ZQJOH w ਐతܕ͚ w ܕͳ͠ͷίʔυͱܕ͖ͭͷίʔυΛࠞͥΔ w ͩΜͩΜܕΛ͚͍ͯ͘ w ܕॻ͘
ܕͳ͠ݴޠͷͨΊͷܕ w ͦΕͳΓʹڧྗͳܕγεςϜ w 6OJPOUZQFT qPXTFOTJUJWFUZQJOH HFOFSJDT w μοΫλΠϐϯά
w ߏత෦ܕ w ΫϥεͷܧঝؔΛ͋ͱͰมߋͰ͖ΔΈ )BTLFMM 4XJGU $ w (SBEVBM5ZQJOH
ΞτϥΠϯ w 3VCZͷ w ܕͳ͠ݴޠͷͨΊͷܕ w 5ZQF4DSJQUͱ࠷ۙͷྲྀΕ w 3VCZͷͨΊͷܕ
࠷ۙͷྲྀߦ w ܕ͖ͭͷํݴ w 5ZQF4DSJQU 5ZQFE4DIFNF w ܕऍͷαϙʔτ w 1ZUIPO
1)1
5ZQF4DSJQU w ݱࡏͰҰ൪উ͍ͬͯΔܕ͖ͭํݴ w λʔήοτݴޠ+4ʢϥϯλΠϜ+4ʣ class Drawer<ClothingType> { contents: ClothingType[]
= []; add(object: ClothingType) { this.contents.push(object); } } interface Sock { color: string } interface TShirt { size: "s" | "m" | "l" } const drawer: Drawer<Sock> = new Drawer() drawer.push({ color: "white" }) drawer.push({ size: "s" })
5ZQF4DSJQUͷ͍͢͝ͱ͜Ζ w ܕͳ͠ݴޠʹܕΛ͚Δਓྨͷເ͕ͬͨ w ΈΜͳܕΛॻ͍͍ͯΔͷ͕͍͢͝ w ߏతͳܕ͚ͩͰͳΜͱ͔ͳͬͯΔͷ͕͍͢͝ w 6OJPOUZQFTͱ͔qPXTFOTJUJWFUZQFTͱ͔MPHJDBMUZQFTͱ͔͠Εͬͱ ೖͬͯΔͷ͕͍͢͝
w ϥϯλΠϜͱͷ੍ΛΓӽ͑ͯಈ͍ͯΔͷ͕͍͢͝
ܕॻ͔ͳ͍͚ͯ͘ͳ͍ w ΑͬΆͲࣗ໌ͳͷҎ֎ॻ͘ w ϥΠϒϥϦͷܕEFpOJUFMZUZQFEʹू·͍ͬͯΔ const numbers: number[] = [1,2,3]
numbers.map<string>(x => x.toString())
jQuery.d.ts
ߏతͳܕ w ΫϥεͷܧঝؔͰͳ͘ɺϝιουͷू߹Ͱ෦ܕؔΛఆ͢Δ w ߏతͳܕ͚ͩͱ͍͏ͷ͍͠ w ͋Δ interface ForEach<A> {
forEach: (fun: (a: A) => void) => void } const x: ForEach<number> = [1,2,3]
ͨ·ͨ·ಉ͡ߏ͕͋ͬͨΒʁ w 5γϟπͱζϘϯ͕۠ผͰ͖ͳ͘ͳΔ w #SBOEFEUZQFͰରԠ͢Δ interface TShirt { size: "s"
| "m" | "l"; } interface Pants { size: "s" | "m" | "l"; }
ͨ·ͨ·ಉ͡ߏ͕͋ͬͨΒʁ w 5γϟπͱζϘϯ͕۠ผͰ͖ͳ͘ͳΔ w #SBOEFEUZQFͰରԠ͢Δ interface TShirt { size: "s"
| "m" | "l"; } interface Pants { size: "s" | "m" | "l"; } interface TShirt { type: "tshirt" size: "s" | "m" | "l"; } interface Pants { type: "pants" size: "s" | "m" | "l"; }
6OJPO5ZQFT'MPXTFOTJUJWF5ZQJOH w OVMMVOEFpOFEͱ͏·͘ΕΔΑ͏ʹͳͬͨ const x: string | undefined = ...
// Object is possibly 'undefined'. x.toUpperCase() if (x) { x.toUpperCase() }
ϥϯλΠϜͷ੍ w +4ͷϥϯλΠϜʹͰ͖ͳ͍͜ͱ54ͰͰ͖ͳ͍ w ʮ͋Δ͕ΠϯλʔϑΣʔεʹద߹͍ͯ͠Δ͔ݕ͍ࠪͨ͠ʯ w Ϣʔβʔ͕ࣗͷͰݕূ͢Δ -PHJDBMUZQFT •
function isPants(object: any): object is Pants { return object.type == "pants" }
ݟ w ਓؒܕΛॻ͘ w ʢܕΛॻ͖ͨ͘ͳ͍ਓ5ZQF4DSJQUΛΘͳ͍ͷͰ؍ଌͰ͖ͳ͍ʣ w େମ+BWB4DSJQUͷίʔυʹܕ͕ͭ͘ w 6OJPOUZQFTͱ͔qPXTFOTJUJWFUZQJOHͱ͔͕ॏཁ w
ߏత෦ܕಈ͘ w ϥϯλΠϜͷ੍ݶΛΓӽ͑ΔͨΊ JTܕ
1ZUIPO 1)1 w ϝιουͷܕΛॻ͘ه๏͕ಋೖ͞Εͨ w ܕݕ࣮ࠪߦ࣌ w ϝιουݺͼग़͠ͷͱ͖ʹܕݕࠪ͢Δ w ੩తͳܕݕࠪث։ൃ͞Ε͍ͯΔ
function sum(int $a, int $b) { return $a + $b; }
ΞτϥΠϯ w 3VCZͷ w ܕͳ͠ݴޠͷͨΊͷܕ w 5ZQF4DSJQUͱ࠷ۙͷྲྀΕ w 3VCZͷͨΊͷܕ
3VCZͷͨΊͷܕ w 4PGUUZQJOHͷݟˠਪΛؤுΔͷઓతʢ!NBNFʣ w 5ZQF4DSJQUͷݟ w ਓྨܕΛॻ͘ w ͦΕͳΓʹڧྗͳܕγεςϜ͕͋Ε্ख͘Εͦ͏ w
1ZUIPO 1)1ͷݟ w ܕऍܾΊͳ͍ͱ͍͚ͳ͍ w ܕݕࠪثผπʔϧʹ͢Δ
ܕऍͷه๏ 3#4 w গͳ͘ͱϥΠϒϥϦͷܕΛॻ͘ํ๏͕ඞཁ w ܕݕࠪπʔϧ͕ͦΕͧΕʹ༻ҙ͢Δͷ͔ͳΓݫ͍͠ w ϥϯλΠϜͷݕ͕ࠪͰ͖Δ͚ͩͰͦΕͳΓʹخ͍͠Ͱʁ w ιʔείʔυʹຒΊࠐΉߏจ࠾༻͠ͳ͍
ΈࠐΈͷܕऍߏจʢෆ࠾༻ʣ class Box extend T::Sig extend T::Generic Elem = type_member
sig {returns(Elem)} # @type var box: Box[Integer] box = Box.new box.x = "hello" &NCFEEFE%4- 4PSCFU $PNNFOUT 4UFFQ class Box [A] def value: A; ...; end def value=(value: A): A; ...; end end a: Box[Integer] = Box.new
ͦΕͳΓʹϦονͳܕ w λϓϧɺϨίʔυ [Integer, Symbol] w Ϧςϥϧܕ 1, "hello", :world
w 6OJPOUZQFT Integer | String w 0QUJPOBMUZQFTɺ(FOFSJDTʜ Array[String]? w յΕ͍ͯΔͷJODPNQBUJCMF incompatible def select: [A] () { (Elem) -> A
μοΫλΠϐϯά w JOUFSGBDFΛఆٛͯ͠ɺཁٻ͞ΕΔϝιουͷू߹Λॻ͘ w ϝιου͕͋Δ͔ͳ͍͔Ͱݕࠪ͞ΕΔ interface _Push def <<: (String)
-> void end def foo: (_Push) -> void def foo(a) a << "hello" end foo [] foo "" foo 3 # Τϥʔ
࣮ߦ࣌ݕ͚ࠪͩͰ w γάωνϟΛॻ͍ͨΒɺϢχοτςετͰςετ͢Δํ๏Λఏڙ w ϝιουݺͼग़͠ͷલޙʹܕݕࠪΛૠೖ w IUUQTSIDDPOOQBTTDPNFWFOU $ RBS_TEST_TARGET='Goodcheck::*' \
RBS_TEST_RAISE=true \ RUBYOPT='-rbundler/setup -rruby/signature/test/setup' \ RBS_TEST_OPT='-rset -rpathname -Isig' \ bundle exec rake test
IUUQTHJUIVCDPNTJEFSHPPEDIFDLUSFFSCT
·ͱΊ w 4PGUUZQJOHΈ͍ͨʹਪΛؤுΔͷ߹ཧతͰͳ͍ w ܕΛॻ͔ͳ͍ͷݫ͍͠ w ਓؒܕΛॻ͘ʢ͜͜ͰಘΒΕͨݟʣ w 3VCZͷͨΊͷܕ w
ΘΓͱϦονͳܕ(FOFSJDTɺ6OJPOUZQFTɺ'MPXTFOTJUJWFUZQJOH w μοΫλΠϐϯάؤுΔʢߏత෦ܕʣ
w <$BSUXSJHIU >3$BSUXSJHIUBOE.'BHBO4PGUUZQJOH w <.BUTVNPUP >4.BUTVNPUPBOE:.JOBNJEF5ZQF*OGFSFODFGPS3VCZ 1SPHSBNTCBTFEPO1PMZNPSQIJD3FDPSE5ZQFT w <+FOTFO >4)+FOTFOBOE".MMFSBOE15IJFNBOO
5ZQF"OBMZTJTGPS +BWB4DSJQU w <4JFL >+4JFLBOE85BIB (SBEVBM5ZQJOHGPS0CKFDUT w <)PDITUBEU >45)PDITUBEU 5ZQFE4DIFNF'SPN4DSJQUTUP1SPHSBNT w