Save 37% off PRO during our Black Friday Sale! »

Rubyに型があると便利か

77897395d9b84463bd474a69b922b2ec?s=47 ksss
May 28, 2016

 Rubyに型があると便利か

2016/5/28 東京Ruby会議11 3rd session

77897395d9b84463bd474a69b922b2ec?s=128

ksss

May 28, 2016
Tweet

Transcript

  1. Rubyʹܕ͕͋Δͱ ศ ར ͔ ೋʓҰ࿡೥ ޒ݄ೋेീ೔ ౦ژRubyձٞ11 ksss

  2. p self Yuki Kurihara ! ksss " @_ksss_ RubyKaja 2014

    (Asakusa.rb) Ұࣇͷύύ
  3. spicelife

  4. झຯ == Hacking ! ksss/AsciiPack ! ksss/digest-murmurhash ! ksss/rgot !

    ksss/mruby-file-stat ! ksss/active_tsv
  5. ܕ

  6. None
  7. ܕͱݴͬͯ΋͍Ζ͍Ζ͋Δ

  8. Rubyʹܕ͕͋Δͱศར͔ RubyͰclassνΣοΫ͢Δͱ w w w w w w w w

    w w w ศར͔
  9. RubyͰclassνΣοΫ

  10. String === "hello" #=> true String === 123 #=> false

    String === nil #=> false RubyͰClassνΣοΫ
  11. ͜Ε·ͰͷRubyͰͷ࣮૷ྫ ! gogotanaka/Rubype ! egonSchiele/contracts.ruby def sum(x, y) x +

    y end typesig :sum, [Numeric, Numeric] => Numeric
  12. TypeStruct ! ksss/type_struct

  13. First Issue σʔλͷմ͚ͩΛද͢class͕΄͔ͬͨ͠ ͍͍࣮ͪͪ૷͢Δͷ͕ΊΜͲ͍͘͞ ݁ߏ௕ੜ͖͢Δ ·͕ͪͬͯ࢖ΘΕΔͱࠔΔ Keyword argument͕࢖͑ΔStruct

  14. TypeStruct Α͏͸ΦϨΦϨStruct `new`ͰKeyword argument͕࢖͑Δ C΍GolangͷΑ͏ͳܕه๏

  15. Struct͓͞Β͍ User = Struct.new( :name, :age, :type, ) user =

    User.new( "ksss", 29, "normal", ) user.name #=> "ksss"
  16. TypeStruct User = TypeStruct.new( name: String, age: Integer, type: /\A(genius|normal)\z/

    ) user = User.new( name: "ksss", age: 29, type: "normal" ) user.name #=> "ksss"
  17. TypeStruct name: String, name: "ksss", String === "ksss" #=> true

  18. TypeStruct age: Integer, age: 29, Integer === 29 #=> true

  19. TypeStruct type: /\A(genius|normal)\z/ type: "normal" /\A(genius|normal)\z/ === "normal" #=> true

  20. TypeStruct νΣοΫ͸஋͕มΘΔ͚࣌ͩ νΣοΫ͕௨Βͳ͔ͬͨΒଈ࠲ʹΤϥʔ User = TypeStruct.new( name: String, age: Integer,

    ) User.new(name: "ksss", age: "30") #=> TypeError user.age = "30" #=> TypeError
  21. Kernel#===ศར Rubyͷ΄ͱΜͲͷObjectͰΑ΂Δ (BasicObjectҎ֎) Rubyͷclassܧঝߏ଄͕࢖͑Δ Numeric === 1(Integer) or 1.23(Float) ͳΜͰ΋Α͚Ε͹

    `Object`
  22. TypeStruct ਖ਼௚͜Ε͚ͩͰ͸ࣗ෼Ͱ΋࢖Θͳ͔ͬͨ ͋·Γ࢖͍Έ͕ͪͳ͍ ͜Ε͚ͩͷͨΊʹgemΛೖΕͳͯ͘΋

  23. None
  24. Second Issue JSONΛѻ͏ϓϩάϥϜΛॻ͘ͷ͕ΊΜͲ͘ ͍͞

  25. Second Issue { "object": { "object_id" 123, "contents": [ {

    "content_id": 234, "items": [ { "type": "image", "image_id": 10, "position": { "x": 100, "y": 200, } }, { "type": "text", "font_id": 20, "position": { "x": 300, "y": 400, } "option": {} }]}]}}
  26. Second Issue json["object"]["contents"].each do |content| content["items"].each do |item| case item["type"]

    when "image" image_id = item["image_id"] x = item["position"]["x"] # ... when "text" # ... end end end
  27. Second Issue json.fetch("object").fetch("contents").each do |content| content.fetch("items").each do |item| case item.fetch("type")

    when "image" image_id = item.fetch("image_id") x = item.fetch("position").fetch("x") # ... when "text" # ... end end end
  28. Second Issue

  29. Second Issue typo͕͜Θ͍ ίʔυ͕ಡΈʹ͍͘ Ͳ͏͍͏ϝιου͕࢖͑Δͷ͔Θ͔Βͳ͍ nil͸͋Γ͑Δͷ͔ͳͲ͕Θ͔Βͳ͍ ଞͷݴޠ͸Ͳ͏ͯ͠Δʁ

  30. Golang JSON type Point struct { X int `json:"x"` Y

    int `json:"y"` } type Line struct { P1 Point `json:"p1"` P2 Point `json:"p2"` } func main() { const input = `{"p1":{"x":10,"y":20},"p2":{"x":30,"y":40}}` var line Line json.NewDecoder(strings.NewReader(input)).Decode(&line) fmt.Println(line.P2.Y) //=> 40 }
  31. Crystal JSON class Point JSON.mapping({ x: Int64, y: Int64, })

    end class Line JSON.mapping({ p1: Point, p2: Point, }) end input = %({"p1":{"x":10,"y":20},"p2":{"x":30,"y":40}}) line = Line.from_json(input) puts line.p2.y #=> 40
  32. Second Issue RubyͰJSON(Hash)ΛStructԽ͢Δ JSON.parse(json, object_class: Foo)ͩͱɺ࠶ ؼతʹॻ͘ͷ͕໘౗ ͳΜ͔͖ͬ͞ͷه๏ɺ࠷ۙݟͨͧ……ʁ TypeStructʹػೳ௥Ճͯ͠ΈΕ͹͍͍ͷͰ͸ʂ

  33. Second Issue

  34. TypeStruct.from_hash Point = TypeStruct.new( x: Integer, y: Integer, ) Line

    = TypeStruct.new( p1: Point, p2: Point, ) input = %({"p1":{"x":10,"y":20}, "p2":{"x":30,"y":40}}) line = Line.from_hash(JSON(input)) puts line.p2.y #=> 40
  35. { "object": { "object_id" 123, "contents": [ { "content_id": 234,

    "items": [ { "type": "image", "image_id": 10, "position": { "x": 100, "y": 200, } }, { "type": "text", "font_id": 20, "position": { "x": 300, "y": 400, } "option": {} }]}]}} TypeStruct
  36. TypeStruct Position = TypeStruct.new( x: Numeric, y: Numeric, ) Text

    = TypeStruct.new( font_id: Integer, position: Position, option: Option, ) Image = TypeStruct.new( image_id: Integer, position: Position, ) Content = TypeStruct.new( content_id: Integer, items: ArrayOf(Image | Text), ) Obj = TypeStruct.new( object_id: Integer, contents: ArrayOf(Content), )
  37. TypeStruct json.object.contents.each do |content| content.items.each do |item| case item when

    Image image_id = item.image_id x = item.position.x # ... when Text # ... end end end
  38. TypeStruct ΈͨΊΑ͠ ҆৺ײ͕͋Δ શͯͷJSONίʔυΛTypeStructʹ͓͖͔͑ ΕΔͷͰ͸ʁ ͍͔ͭ͘ͷ໰୊఺

  39. ~4ͭͷissueͱ4ͭͷclass~ TypeStruct

  40. [ {"slime": "A"}, {"slime": "B"}, {"slime": "C"} ] Arrayがあらわれた!

  41. Array? Slime = TypeStruct.new( slime: String ) Encount = TypeStruct.new(

    enemies: Array # []Slime )
  42. Golang & Crystal class Encount JSON.mapping({ enemies: Array(Slime) }) end

    type Encount struct { Enemies []Slime }
  43. ArrayOf

  44. ArrayOf Encount = TypeStruct.new( enemies: ArrayOf(Slime) ) encount = Encount.from_hash(

    enemies: [ {"slime": "A"}, {"slime": "B"}, {"slime": "C"} ] ) encount.enemies #=> [#<Slime A>, #<Slime B>, #<Slime C>]
  45. ArrayOf class ArrayOf def initialize(type) @type = type end def

    ===(ary) ary.all? { |o| @type === o } end end
  46. ArrayOf ͘͠Έ͸୯७ ʮ***ͷArrayʯΛදݱͰ͖Δ ͜Ε͚ͩͰ΋࢖͑Δ͔΋ʁ case ary when ArrayOf(String) # ...

    when ArrayOf(Regexp)
  47. { "1": {"slime": "A"}, "2": {"slime": "B"}, "3": {"slime": "C"}

    } Hashがあらわれた!
  48. Hash? Encount = TypeStruct.new( enemies: Hash )

  49. Golang & Crystal class Encount JSON.mapping({ enemies: Hash(String, Slime) })

    end type Encount struct { Enemies map[string]Slime }
  50. HashOf

  51. HashOf Encount = TypeStruct.new( enemies: HashOf(String, Slime) ) encount =

    Encount.from_hash( enemies: { "1" => {"slime": "A"}, "2" => {"slime": "B"}, "3" => {"slime": "C"} } ) encount.enemies.each do |index, slime|
  52. HashOf class HashOf def initialize(key_type, value_type) @key_type = key_type @value_type

    = value_type end def ===(h) h.all? do |k, v| @key_type === k && @value_type === v end end end
  53. Booleanがあらわれた! Nullがあらわれた! [ true, false, null ]

  54. Golang & Crystal type Figure struct { Show bool }

    class Figure JSON.mapping({ show: Bool }) end
  55. include Boolean? module Boolean end class TrueClass include Boolean end

    class FalseClass include Boolean end Boolean === true #=> true Boolean === false #=> true Boolean === Object.new #=> false
  56. Null type Canvas struct { item: Item } class Canvas

    item: { type: Item, nilable: true } end
  57. Slimeがあらわれた! [ {"slime": "A"}, {"name": "drakee"}, {"hp": 100, "type": "Golem"}

    ] Drakeeがあらわれた! Golemがあらわれた!
  58. Union

  59. Union class Union def initialize(*classes) @classes = classes end def

    ===(obj) @classes.any? { |c| c === obj } end end
  60. Union module Ext refine Class do def |(other) Union.new(self, other)

    end end end using Ext
  61. Union Integer | String # 123 or "hi" TrueClass |

    FalseClass # true or false ArrayOf(Item) | NilClass # [Item] or nil ArrayOf(Slime | Drakee | Golem) # [Slime or Drakee or Golem]
  62. Union Crystalͷจ๏͔Βഈआ `Class#|`Λఆٛ͢ΔͱͦΕͬΆ͘ͳΔ ޙʹ໰୊ࣇͱͳΔ

  63. ダックタイピングがあらわれた!

  64. Interface

  65. Interface class Interface def initialize(*methods) @methods = methods end def

    ===(other) @methods.all? do |m| other.respond_to?(m) end end end
  66. Interface Reader = Interface.new(:read) Writer = Interface.new(:write) Stream = TypeStruct.new(

    input: Reader, output: Writer, ) Stream.new(input: $stdin, output: StringIO.new)
  67. Interface μοΫλΠϐϯά༻ golangͷΑ͏ʹclassͱͷґଘΛͳ͍ͨ͘͠ ϝιου͕ఆٛ͞Ε͍ͯΔ͔͚ͩΛνΣοΫ ࡞ͬͯΈ͚ͨͲͦΜͳʹ࢖͏৔໘͕ࢥ͍ු͔͹ͳ ͍(·͓ͩͨͤͯͳ͍) ར༻γʔϯΛ໛ࡧத

  68. None
  69. TypeStructͷϝϦοτ JSONΛTypeStructʹ౰ͯ͸ΊΔ͜ͱ͕Ͱ͖Δ StructͷϝϦοτ͸ͦͷ··ڗड typoରࡦ, ίʔυͷݟͨ໨, σόοάͷ͠΍ ͢͞ ୯७ͳ࡞ΓͳͷͰɺগͣͭ͠ಋೖ΋Ͱ͖Δ

  70. TypeStructͷϝϦοτ ಈ࡞͢ΔυΩϡϝϯτͱͯ͠ศར هԱʹཔΒͳ͍͍ͯ͘ ৽ਓ͔Βͷʮ͜ͷJSONͬͯͲ͏͍͏ߏ଄͕͋ Γ͑ΔΜͰ͔͢ʁʯͱ͍͏࣭໰ʹଈ౴Ͱ͖Δɻ PRͰܕͷมߋ఺Λٞ࿦Ͱ͖Δ

  71. TypeStructͷσϝϦοτ Union͸ॾਕͷ݋ σόοά͕͠ʹ͘͘ͳΔ ͍͋·͍͕͞࢈·ΕΔ ܕΛॻ͘ͷ͕ΊΜͲ͍͘͞ ͪΐͬͱͨ͠΋ͷΑΓɺෳ਺ਓͰͷ։ൃ޲͖

  72. TypeStruct·ͱΊ ݱঢ়͸JSONΛclassνΣοΫͭͭ͠Objectʹ ͢Δ΋ͷͱͯ͠࢖͍ͬͯΔ ෳ਺ਓͰΧονϦͱ࡞ΔͷʹΉ͍ͯΔ Scope͕޿͍৔߹ʹศར

  73. ࣮૷Ͱ໎ͬͨॴ nilΛڐՄ͢΂͖͔ɺͤ͟Δ΂͖͔ UnionΛڐ͢΂͖͔ ࣮૷ଆ΋࢖͏ଆ΋ෳࡶʹͳΔ crystal΍golangͰ͸ɺJSONม׵ʹ͸࢖͑ͳ͘ͳ͍ͬͯΔ nilͱBoolean͑͞ͳΜͱ͔ͳΕ͹ɺ͍Βͳ͍͔΋ ҉໧ͷม׵Λ͢΂͖͔

  74. Rubyʹܕ͕͋Δͱ ศར͔ʁ

  75. ศར

  76. Ruby3

  77. Ruby3 http://www.slideshare.net/yukihiro_matz/ feeding-the-sharks/143 http://www.atdot.net/~ko1/activities/ 2016_RubyConfLT_pub.pdf

  78. ※ Ruby3ͷܕ͸ ࠓճͷൃදͷΑ͏ͳ΋ͷͱ͸ શ͘ҧ͍·͢

  79. Ruby3(ͷల๬Λฉ͍ͨ࿩) ੩తνΣοΫ => method͕ݺ΂Δ͔ͷࣄલcheck RubyϨϕϧͰ͸AnnotationΛॻ͔ͳ͍ => def meth(arg : Foo)Έ͍ͨͳ͜ͱ͸͠ͳ͍

    CϨϕϧͷmethod͸શ෦AnnotationΛॻ͘ => େมͦ͏ɻɻɻ
  80. ͜Ε͔Β ੩తղੳ͸௅ઓͯ͠Έ͍ͨ TAPLಡΉͷ͕Μ͹Δ Rubyͷ͘͠ΈΛֶͿ ଞͷݴޠʹ΋৮Ε͍ͯ͘

  81. ͓ΘΓ