Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rubyに型があると便利か

ksss
May 28, 2016

 Rubyに型があると便利か

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

ksss

May 28, 2016
Tweet

More Decks by ksss

Other Decks in Programming

Transcript

  1. Rubyʹܕ͕͋Δͱ


    ͔
    ೋʓҰ࿡೥ ޒ݄ೋेീ೔
    ౦ژRubyձٞ11
    ksss

    View Slide

  2. p self
    Yuki Kurihara
    ! ksss
    " @_ksss_
    RubyKaja 2014 (Asakusa.rb)
    Ұࣇͷύύ

    View Slide

  3. spicelife

    View Slide

  4. झຯ == Hacking
    ! ksss/AsciiPack
    ! ksss/digest-murmurhash
    ! ksss/rgot
    ! ksss/mruby-file-stat
    ! ksss/active_tsv

    View Slide

  5. ܕ

    View Slide

  6. View Slide

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

    View Slide

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

    View Slide

  9. RubyͰclassνΣοΫ

    View Slide

  10. String === "hello" #=> true
    String === 123 #=> false
    String === nil #=> false
    RubyͰClassνΣοΫ

    View Slide

  11. ͜Ε·ͰͷRubyͰͷ࣮૷ྫ
    ! gogotanaka/Rubype
    ! egonSchiele/contracts.ruby
    def sum(x, y)
    x + y
    end
    typesig :sum, [Numeric, Numeric] => Numeric

    View Slide

  12. TypeStruct
    ! ksss/type_struct

    View Slide

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

    View Slide

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

    View Slide

  15. Struct͓͞Β͍
    User = Struct.new(
    :name,
    :age,
    :type,
    )
    user = User.new(
    "ksss",
    29,
    "normal",
    )
    user.name #=> "ksss"

    View Slide

  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"

    View Slide

  17. TypeStruct
    name: String,
    name: "ksss",
    String === "ksss" #=> true

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  21. Kernel#===ศར
    Rubyͷ΄ͱΜͲͷObjectͰΑ΂Δ
    (BasicObjectҎ֎)
    Rubyͷclassܧঝߏ଄͕࢖͑Δ
    Numeric === 1(Integer) or 1.23(Float)
    ͳΜͰ΋Α͚Ε͹ `Object`

    View Slide

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

    View Slide

  23. View Slide

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

    View Slide

  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": {}
    }]}]}}

    View Slide

  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

    View Slide

  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

    View Slide

  28. Second Issue

    View Slide

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

    View Slide

  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
    }

    View Slide

  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

    View Slide

  32. Second Issue
    RubyͰJSON(Hash)ΛStructԽ͢Δ
    JSON.parse(json, object_class: Foo)ͩͱɺ࠶
    ؼతʹॻ͘ͷ͕໘౗
    ͳΜ͔͖ͬ͞ͷه๏ɺ࠷ۙݟͨͧ……ʁ
    TypeStructʹػೳ௥Ճͯ͠ΈΕ͹͍͍ͷͰ͸ʂ

    View Slide

  33. Second Issue

    View Slide

  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

    View Slide

  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

    View Slide

  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),
    )

    View Slide

  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

    View Slide

  38. TypeStruct
    ΈͨΊΑ͠
    ҆৺ײ͕͋Δ
    શͯͷJSONίʔυΛTypeStructʹ͓͖͔͑
    ΕΔͷͰ͸ʁ
    ͍͔ͭ͘ͷ໰୊఺

    View Slide


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

    View Slide

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

    View Slide

  41. Array?
    Slime = TypeStruct.new(
    slime: String
    )
    Encount = TypeStruct.new(
    enemies: Array # []Slime
    )

    View Slide

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

    View Slide

  43. ArrayOf

    View Slide

  44. ArrayOf
    Encount = TypeStruct.new(
    enemies: ArrayOf(Slime)
    )
    encount = Encount.from_hash(
    enemies: [
    {"slime": "A"},
    {"slime": "B"},
    {"slime": "C"}
    ]
    )
    encount.enemies
    #=> [#, #, #]

    View Slide

  45. ArrayOf
    class ArrayOf
    def initialize(type)
    @type = type
    end
    def ===(ary)
    ary.all? { |o| @type === o }
    end
    end

    View Slide

  46. ArrayOf
    ͘͠Έ͸୯७
    ʮ***ͷArrayʯΛදݱͰ͖Δ
    ͜Ε͚ͩͰ΋࢖͑Δ͔΋ʁ
    case ary
    when ArrayOf(String)
    # ...
    when ArrayOf(Regexp)

    View Slide

  47. {
    "1": {"slime": "A"},
    "2": {"slime": "B"},
    "3": {"slime": "C"}
    }
    Hashがあらわれた!

    View Slide

  48. Hash?
    Encount = TypeStruct.new(
    enemies: Hash
    )

    View Slide

  49. Golang & Crystal
    class Encount
    JSON.mapping({
    enemies: Hash(String, Slime)
    })
    end
    type Encount struct {
    Enemies map[string]Slime
    }

    View Slide

  50. HashOf

    View Slide

  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|

    View Slide

  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

    View Slide

  53. Booleanがあらわれた!
    Nullがあらわれた!
    [ true, false, null ]

    View Slide

  54. Golang & Crystal
    type Figure struct {
    Show bool
    }
    class Figure
    JSON.mapping({
    show: Bool
    })
    end

    View Slide

  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

    View Slide

  56. Null
    type Canvas struct {
    item: Item
    }
    class Canvas
    item: { type: Item, nilable: true }
    end

    View Slide

  57. Slimeがあらわれた!
    [
    {"slime": "A"},
    {"name": "drakee"},
    {"hp": 100, "type": "Golem"}
    ]
    Drakeeがあらわれた!
    Golemがあらわれた!

    View Slide


  58. View Slide

  59. Union

    View Slide

  60. Union
    class Union
    def initialize(*classes)
    @classes = classes
    end
    def ===(obj)
    @classes.any? { |c| c === obj }
    end
    end

    View Slide

  61. Union
    module Ext
    refine Class do
    def |(other)
    Union.new(self, other)
    end
    end
    end
    using Ext

    View Slide

  62. 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]

    View Slide

  63. Union
    Crystalͷจ๏͔Βഈआ
    `Class#|`Λఆٛ͢ΔͱͦΕͬΆ͘ͳΔ
    ޙʹ໰୊ࣇͱͳΔ

    View Slide

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

    View Slide

  65. Interface

    View Slide

  66. Interface
    class Interface
    def initialize(*methods)
    @methods = methods
    end
    def ===(other)
    @methods.all? do |m|
    other.respond_to?(m)
    end
    end
    end

    View Slide

  67. Interface
    Reader = Interface.new(:read)
    Writer = Interface.new(:write)
    Stream = TypeStruct.new(
    input: Reader,
    output: Writer,
    )
    Stream.new(input: $stdin, output: StringIO.new)

    View Slide

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

    View Slide

  69. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  76. ศར

    View Slide

  77. Ruby3

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  81. ͜Ε͔Β
    ੩తղੳ͸௅ઓͯ͠Έ͍ͨ
    TAPLಡΉͷ͕Μ͹Δ
    Rubyͷ͘͠ΈΛֶͿ
    ଞͷݴޠʹ΋৮Ε͍ͯ͘

    View Slide

  82. ͓ΘΓ

    View Slide