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

clojure.specの話

 clojure.specの話

2016/07/29 Lisp meetup #42 の発表資料です。

OHTA Shogo

July 29, 2016
Tweet

More Decks by OHTA Shogo

Other Decks in Programming

Transcript

  1. DMPKVSFTQFDͷ࿩
    -JTQNFFUVQ
    !BUIPT

    View Slide

  2. ࣗݾ঺հ
    ‣ 5XJUUFS!BUIPT
    ‣ χϟϯύεגࣜձࣾॴଐ
    ‣ $MPKVSFίϯτϦϏϡʔλ

    View Slide

  3. DMPKVSFTQFD
    ‣ ࣍ظϦϦʔεͰͷಋೖ͕ਐΊΒΕ͍ͯΔ৽ػೳ
    ‣ ੩తܕͰͳ͘ɺड़ޠͷ૊Έ߹ΘͤʹΑͬͯσʔλ
    ܕɾؔ਺ͷ࢓༷Λهड़͢Δ
    ‣ Ұ౓࢓༷ εϖοΫ
    Λॻ͚͹ҰཻͰԿ౓΋͓͍͍͠
    υΩϡϝϯςʔγϣϯ
    ܖ໿ϓϩάϥϛϯά
    ϓϩύςΟϕʔεςετ
    ϚΫϩͷߏจνΣοΫ

    View Slide

  4. DMPKVSFTQFDͷ࢖͍ํ
    ‣ [org.clojure/clojure “1.9.0-alphaXX”]
    Λ:dependenciesʹ௥Ճ
    ‣ ࠷৽൛͸BMQIB ݱࡏ

    ‣ (require ‘[clojure.spec :as s])

    View Slide

  5. όϦσʔλͱͯ͠ͷ
    DMPKVSFTQFD

    View Slide

  6. ड़ޠ
    ‣ ड़ޠ CPPMΛฦؔ͢਺
    ͸ͦͷ··ͰεϖοΫͱͯ͠
    ࢖͑Δ
    user=> (s/valid? integer? 42)

    View Slide

  7. ड़ޠ
    ‣ ड़ޠ CPPMΛฦؔ͢਺
    ͸ͦͷ··ͰεϖοΫͱͯ͠
    ࢖͑Δ
    user=> (s/valid? integer? 42)
    true
    user=> (s/valid? integer? “foo”)

    View Slide

  8. ड़ޠ
    ‣ ड़ޠ CPPMΛฦؔ͢਺
    ͸ͦͷ··ͰεϖοΫͱͯ͠
    ࢖͑Δ
    user=> (s/valid? integer? 42)
    true
    user=> (s/valid? integer? “foo”)
    false
    user=>

    View Slide

  9. WBMJE FYQMBJO
    ‣ valid?͸εϖοΫΛຬ͔ͨ͢Ͳ͏͔Λฦ͢
    ‣ explain͸εϖοΫΛຬͨ͞ͳ͍ՕॴΛࢦఠ͢Δ
    user=> (s/valid? integer? “foo”)
    false
    user=> (s/explain integer? “foo”)

    View Slide

  10. WBMJE FYQMBJO
    ‣ valid?͸εϖοΫΛຬ͔ͨ͢Ͳ͏͔Λฦ͢
    ‣ explain͸εϖοΫΛຬͨ͞ͳ͍ՕॴΛࢦఠ͢Δ
    user=> (s/valid? integer? “foo”)
    false
    user=> (s/explain integer? “foo”)
    val: "foo" fails predicate: :clojure.spec/unknown
    nil
    user=> (s/explain integer? 42)

    View Slide

  11. WBMJE FYQMBJO
    ‣ valid?͸εϖοΫΛຬ͔ͨ͢Ͳ͏͔Λฦ͢
    ‣ explain͸εϖοΫΛຬͨ͞ͳ͍ՕॴΛࢦఠ͢Δ
    user=> (s/valid? integer? “foo”)
    false
    user=> (s/explain integer? “foo”)
    val: "foo" fails predicate: :clojure.spec/unknown
    nil
    user=> (s/explain integer? 42)
    Success!
    nil
    user=>

    View Slide

  12. εϖοΫͷ߹੒
    ‣ and΍orΛ࢖ͬͯෳ਺ͷεϖοΫΛ૊Έ߹ΘͤՄೳ
    user=> (s/valid? (s/and integer? even?) 0)
    true
    user=> (s/valid? (s/and integer? even?) 1)
    false

    View Slide

  13. εϖοΫͷ߹੒
    ‣ and΍orΛ࢖ͬͯෳ਺ͷεϖοΫΛ૊Έ߹ΘͤՄೳ
    user=> (s/valid? (s/and integer? even?) 0)
    true
    user=> (s/valid? (s/and integer? even?) 1)
    false
    user=> (s/valid? (s/or :int integer?
    :str string?)
    “foo”)

    View Slide

  14. εϖοΫͷ߹੒
    ‣ and΍orΛ࢖ͬͯෳ਺ͷεϖοΫΛ૊Έ߹ΘͤՄೳ
    user=> (s/valid? (s/and integer? even?) 0)
    true
    user=> (s/valid? (s/and integer? even?) 1)
    false
    user=> (s/valid? (s/or :int integer?
    :str string?)
    “foo”)
    true
    user=>

    View Slide

  15. εϖοΫʹ໊લΛ͚ͭΔ
    ‣ defͰεϖοΫʹ໊લΛ͚ͭΔ͜ͱ͕Ͱ͖Δ
    ‣ εϖοΫࣗମΛ࠶ར༻Ͱ͖Δ
    user=> (s/def ::answer-to-everything
    (fn [x] (= x 42))
    :user/answer-to-everything
    user=> (s/valid? ::answer-to-everything 43)

    View Slide

  16. εϖοΫʹ໊લΛ͚ͭΔ
    ‣ defͰεϖοΫʹ໊લΛ͚ͭΔ͜ͱ͕Ͱ͖Δ
    ‣ εϖοΫࣗମΛ࠶ར༻Ͱ͖Δ
    user=> (s/def ::answer-to-everything
    (fn [x] (= x 42))
    :user/answer-to-everything
    user=> (s/valid? ::answer-to-everything 43)
    false
    user=> (s/valid? ::answer-to-everything 42)

    View Slide

  17. εϖοΫʹ໊લΛ͚ͭΔ
    ‣ defͰεϖοΫʹ໊લΛ͚ͭΔ͜ͱ͕Ͱ͖Δ
    ‣ εϖοΫࣗମΛ࠶ར༻Ͱ͖Δ
    user=> (s/def ::answer-to-everything
    (fn [x] (= x 42))
    :user/answer-to-everything
    user=> (s/valid? ::answer-to-everything 43)
    false
    user=> (s/valid? ::answer-to-everything 42)
    true
    user=>

    View Slide

  18. ίϨΫγϣϯͷεϖοΫ
    ‣ coll-ofmap-ofͰίϨΫγϣϯͷཁૉ͕εϖοΫ
    Λຬ͍ͨͯ͠Δ͔νΣοΫͰ͖Δ
    user=> (s/valid? (s/coll-of integer?) [1 2 3])

    View Slide

  19. ίϨΫγϣϯͷεϖοΫ
    ‣ coll-ofmap-ofͰίϨΫγϣϯͷཁૉ͕εϖοΫ
    Λຬ͍ͨͯ͠Δ͔νΣοΫͰ͖Δ
    user=> (s/valid? (s/coll-of integer?) [1 2 3])
    true
    user=> (s/valid? (s/coll-of integer?) [1 :a])

    View Slide

  20. ίϨΫγϣϯͷεϖοΫ
    ‣ coll-ofmap-ofͰίϨΫγϣϯͷཁૉ͕εϖοΫ
    Λຬ͍ͨͯ͠Δ͔νΣοΫͰ͖Δ
    user=> (s/valid? (s/coll-of integer?) [1 2 3])
    true
    user=> (s/valid? (s/coll-of integer?) [1 :a])
    false
    user=> (s/valid? (s/map-of keyword? integer?)
    {:a 0, :b 1})

    View Slide

  21. ίϨΫγϣϯͷεϖοΫ
    ‣ coll-ofmap-ofͰίϨΫγϣϯͷཁૉ͕εϖοΫ
    Λຬ͍ͨͯ͠Δ͔νΣοΫͰ͖Δ
    user=> (s/valid? (s/coll-of integer?) [1 2 3])
    true
    user=> (s/valid? (s/coll-of integer?) [1 :a])
    false
    user=> (s/valid? (s/map-of keyword? integer?)
    {:a 0, :b 1})
    true
    user=> (s/valid? (s/map-of keyword? integer?)
    {:a 0, :b “foo”})

    View Slide

  22. ίϨΫγϣϯͷεϖοΫ
    ‣ coll-ofmap-ofͰίϨΫγϣϯͷཁૉ͕εϖοΫ
    Λຬ͍ͨͯ͠Δ͔νΣοΫͰ͖Δ
    user=> (s/valid? (s/coll-of integer?) [1 2 3])
    true
    user=> (s/valid? (s/coll-of integer?) [1 :a])
    false
    user=> (s/valid? (s/map-of keyword? integer?)
    {:a 0, :b 1})
    true
    user=> (s/valid? (s/map-of keyword? integer?)
    {:a 0, :b “foo”})
    false
    user=>

    View Slide

  23. ίϨΫγϣϯͷεϖοΫ
    ‣ ΩʔʹΑͬͯ஋ͷܕ͕ҧ͏ϚοϓͷεϖοΫ΋ఆٛՄೳ
    user=> (s/def ::x integer?)
    :user/x
    user=> (s/def ::y string?)
    :user/y
    user=> (s/explain (s/keys :req-un [::x ::y])
    {:x 1, :y 2})

    View Slide

  24. ίϨΫγϣϯͷεϖοΫ
    ‣ ΩʔʹΑͬͯ஋ͷܕ͕ҧ͏ϚοϓͷεϖοΫ΋ఆٛՄೳ
    user=> (s/def ::x integer?)
    :user/x
    user=> (s/def ::y string?)
    :user/y
    user=> (s/explain (s/keys :req-un [::x ::y])
    {:x 1, :y 2})
    In: [:y] val: 2 fails spec: :user/y at: [:y]
    predicate: string?
    nil
    user=> (s/valid? (s/keys :req-un [::x ::y])
    {:x 1, :y “foo”})

    View Slide

  25. ίϨΫγϣϯͷεϖοΫ
    ‣ ΩʔʹΑͬͯ஋ͷܕ͕ҧ͏ϚοϓͷεϖοΫ΋ఆٛՄೳ
    user=> (s/def ::x integer?)
    :user/x
    user=> (s/def ::y string?)
    :user/y
    user=> (s/explain (s/keys :req-un [::x ::y])
    {:x 1, :y 2})
    In: [:y] val: 2 fails spec: :user/y at: [:y]
    predicate: string?
    nil
    user=> (s/valid? (s/keys :req-un [::x ::y])
    {:x 1, :y “foo”})
    true
    user=>

    View Slide

  26. %C$πʔϧͱͯ͠ͷ
    DMPKVSFTQFD

    View Slide

  27. ؔ਺ͷεϖοΫ
    ‣ fizzbuzzؔ਺ͷεϖοΫ͸্ͷΑ͏ʹఆٛͰ͖Δ
    ‣ :args :ret͕ͦΕͧΕࣄલ৚݅ɾࣄޙ৚݅ʹରԠ
    (s/fdef fizzbuzz
    :args (s/cat :n (s/and integer? #(> % 0)))
    :ret (s/or :int integer? :key keyword?))
    (defn fizzbuzz [n]
    (cond (= (mod n 15) 0) :fizzbuzz
    (= (mod n 5) 0) :buzz
    (= (mod n 3) 0) “fizz” ;;←όά
    :else n))

    View Slide

  28. JOTUSVNFOU
    ‣ ؔ਺ʹҾ਺͕εϖοΫΛຬ͍ͨͯ͠Δ͔ͷνΣοΫ
    ΛΦϯɾΦϑͰ͖Δ
    user=> (require ’[clojure.spec.test :as t])
    nil
    user=> (t/instrument)

    View Slide

  29. JOTUSVNFOU
    ‣ ؔ਺ʹҾ਺͕εϖοΫΛຬ͍ͨͯ͠Δ͔ͷνΣοΫ
    ΛΦϯɾΦϑͰ͖Δ
    user=> (require ’[clojure.spec.test :as t])
    nil
    user=> (t/instrument)
    [user/fizzbuzz]
    user=> (fizzbuzz 15)

    View Slide

  30. JOTUSVNFOU
    ‣ ؔ਺ʹҾ਺͕εϖοΫΛຬ͍ͨͯ͠Δ͔ͷνΣοΫ
    ΛΦϯɾΦϑͰ͖Δ
    user=> (require ’[clojure.spec.test :as t])
    nil
    user=> (t/instrument)
    [user/fizzbuzz]
    user=> (fizzbuzz 15)
    “fizzbuzz”
    user=> (fizzbuzz “foo”)

    View Slide

  31. JOTUSVNFOU
    ‣ ؔ਺ʹҾ਺͕εϖοΫΛຬ͍ͨͯ͠Δ͔ͷνΣοΫ
    ΛΦϯɾΦϑͰ͖Δ
    user=> (require ’[clojure.spec.test :as t])
    nil
    user=> (t/instrument)
    [user/fizzbuzz]
    user=> (fizzbuzz 15)
    “fizzbuzz”
    user=> (fizzbuzz “foo”)
    ExceptionInfo Call to #'user/fizzbuzz did not conform to
    spec:
    In: [0] val: "foo" fails at: [:args :n] predicate: integer?
    :clojure.spec/args ("foo") …
    user=>

    View Slide

  32. DIFDL
    ‣ Ҿ਺ͷεϖοΫΛຬͨ͢஋Λࣗಈੜ੒ͯ͠ɺͦͷ஋Λؔ਺ʹ
    ౉ͨ݁͠Ռ͕໭Γ஋ͷεϖοΫΛຬ͔ͨ͢νΣοΫ͢Δ
    user=> (t/check)

    View Slide

  33. DIFDL
    ‣ Ҿ਺ͷεϖοΫΛຬͨ͢஋Λࣗಈੜ੒ͯ͠ɺͦͷ஋Λؔ਺ʹ
    ౉ͨ݁͠Ռ͕໭Γ஋ͷεϖοΫΛຬ͔ͨ͢νΣοΫ͢Δ
    user=> (t/check)
    ({:spec …
    :clojure.spec.test.check/ret
    {:result
    #error {:cause "Specification-based check failed”
    :data {:clojure.spec/problems
    (… {:path [:ret :key],
    :pred keyword?,
    :val “fizz",
    :via [], :in [],
    :clojure.spec.test/args (3),
    :clojure.spec/failure :check-failed}…)}
    user=>

    View Slide

  34. DIFDL
    ‣ Ҿ਺ͷεϖοΫΛຬͨ͢஋Λࣗಈੜ੒ͯ͠ɺͦͷ஋Λؔ਺ʹ
    ౉ͨ݁͠Ռ͕໭Γ஋ͷεϖοΫΛຬ͔ͨ͢νΣοΫ͢Δ
    user=> (t/check)
    ({:spec …
    :clojure.spec.test.check/ret
    {:result
    #error {:cause "Specification-based check failed”
    :data {:clojure.spec/problems
    (… {:path [:ret :key],
    :pred keyword?,
    :val “fizz",
    :via [], :in [],
    :clojure.spec.test/args (3),
    :clojure.spec/failure :check-failed}…)}
    user=>
    Ͱݺͼग़ͨ͠ͱ͖ʹ
    ࣦഊ͢Δ͜ͱΛݕग़

    View Slide

  35. ͕࣌ؒ͋Γͦ͏ͳΒ
    Ԡ༻ྫΛ঺հ

    View Slide

  36. δΣωϨʔλͱͯ͠ͷ
    DMPKVSFTQFD

    View Slide

  37. HFO
    ‣ εϖοΫ͔ΒUFTUDIFDL༻ͷδΣωϨʔλΛ࡞Δ
    ‣ εϖοΫΛຬͨ͢ϥϯμϜͳ஋Λੜ੒Ͱ͖Δ
    ‣ 3&1-Ͱαϯϓϧσʔλ͕΄͍͠ͱ͖ʹศར
    user=> (require ’[clojure.test.check.generators :as
    gen])
    nil
    user=> (gen/generate (s/gen (s/coll-of integer?)))

    View Slide

  38. HFO
    ‣ εϖοΫ͔ΒUFTUDIFDL༻ͷδΣωϨʔλΛ࡞Δ
    ‣ εϖοΫΛຬͨ͢ϥϯμϜͳ஋Λੜ੒Ͱ͖Δ
    ‣ 3&1-Ͱαϯϓϧσʔλ͕΄͍͠ͱ͖ʹศར
    user=> (require ’[clojure.test.check.generators :as
    gen])
    nil
    user=> (gen/generate (s/gen (s/coll-of integer?)))
    [16719156 -26693 47]
    user=> (gen/generate (s/gen (s/coll-of integer?)))

    View Slide

  39. HFO
    ‣ εϖοΫ͔ΒUFTUDIFDL༻ͷδΣωϨʔλΛ࡞Δ
    ‣ εϖοΫΛຬͨ͢ϥϯμϜͳ஋Λੜ੒Ͱ͖Δ
    ‣ 3&1-Ͱαϯϓϧσʔλ͕΄͍͠ͱ͖ʹศར
    user=> (require ’[clojure.test.check.generators :as
    gen])
    nil
    user=> (gen/generate (s/gen (s/coll-of integer?)))
    [16719156 -26693 47]
    user=> (gen/generate (s/gen (s/coll-of integer?)))
    [-158637744 -8 -461005 -238354 59127 -4365]
    user=>

    View Slide

  40. HFOΛ࢖ͬͨϓϩύςΟϕʔεςετ
    ‣ εϖοΫΛຬͨ͢஋Λࣗಈੜ੒͠ɺ͢΂ͯͷ஋ʹର
    ͯ͠ੑ࣭͕੒Γཱ͔ͭͲ͏͔νΣοΫ͢Δ
    (ns fizzbuzz-test
    (:require [clojure.test.check.clojure-test :refer [defspec]]
    [clojure.test.check.properties :as prop]
    [clojure.spec :as s]
    [fizzbuzz :as fb]))
    (defspec fizzbuzz-prop
    (prop/for-all [n (s/gen (s/and integer? #(> % 0)))]
    (let [v (fb/fizzbuzz n)]
    (cond (= (mod n 3) 0) (contains? #{:fizz :fizzbuzz} v)
    (= (mod n 5) 0) (contains? #{:buzz :fizzbuzz} v)
    :else (= n v)))))

    View Slide

  41. ςετ࣮ߦ݁Ռ
    $ lein test

    View Slide

  42. ςετ࣮ߦ݁Ռ
    $ lein test
    lein test fizzbuzz-test
    {:result false, :seed 1469711295643, :failing-size 4, :num-
    tests 5, :fail [96], :shrunk {:total-nodes-visited 6, :depth
    5, :result false, :smallest [3]}, :test-var "fizzbuzz-prop"}
    lein test :only fizzbuzz-test/fizzbuzz-prop
    FAIL in (fizzbuzz-prop) (clojure_test.cljc:21)
    expected: result
    actual: false
    Ran 1 tests containing 1 assertions.
    1 failures, 0 errors.
    Tests failed.
    $

    View Slide

  43. ύʔαͱͯ͠ͷ
    DMPKVSFTQFD

    View Slide

  44. γʔέϯεͷεϖοΫ SFHFY

    ‣ SFHFYͰཁૉͷฒͼʹରͯ͠εϖοΫΛఆٛͰ͖Δ
    user=> (s/valid? (s/* integer?) ’(1 2 3))
    true
    user=> (s/valid? (s/alt :i integer? :s string?)
    ’(“foo”))

    View Slide

  45. γʔέϯεͷεϖοΫ SFHFY

    ‣ SFHFYͰཁૉͷฒͼʹରͯ͠εϖοΫΛఆٛͰ͖Δ
    user=> (s/valid? (s/* integer?) ’(1 2 3))
    true
    user=> (s/valid? (s/alt :i integer? :s string?)
    ’(“foo”))
    true
    user=> (s/valid? (s/cat :i integer? :s string?)
    ’(1 “foo”))

    View Slide

  46. γʔέϯεͷεϖοΫ SFHFY

    ‣ SFHFYͰཁૉͷฒͼʹରͯ͠εϖοΫΛఆٛͰ͖Δ
    user=> (s/valid? (s/* integer?) ’(1 2 3))
    true
    user=> (s/valid? (s/alt :i integer? :s string?)
    ’(“foo”))
    true
    user=> (s/valid? (s/cat :i integer? :s string?)
    ’(1 “foo”))
    true
    user=> (s/valid? (s/cat :i* (s/* integer?)
    :s* (s/* string?))
    ’(1 2 3 “foo” “bar”))

    View Slide

  47. γʔέϯεͷεϖοΫ SFHFY

    ‣ SFHFYͰཁૉͷฒͼʹରͯ͠εϖοΫΛఆٛͰ͖Δ
    user=> (s/valid? (s/* integer?) ’(1 2 3))
    true
    user=> (s/valid? (s/alt :i integer? :s string?)
    ’(“foo”))
    true
    user=> (s/valid? (s/cat :i integer? :s string?)
    ’(1 “foo”))
    true
    user=> (s/valid? (s/cat :i* (s/* integer?)
    :s* (s/* string?))
    ’(1 2 3 “foo” “bar”))
    true
    user=>

    View Slide

  48. DPOGPSN
    ‣ εϖοΫʹ͕ͨͬͯ͠σʔλΛύʔε͢Δ
    ‣ ύʔεʹࣦഊͨ͠Β:clojure.spec/invalid͕ฦΔ
    user=> (s/conform (s/cat :i* (s/* integer?)
    :s* (s/* string?))
    ’(1 2 3 “foo” “bar”))

    View Slide

  49. DPOGPSN
    ‣ εϖοΫʹ͕ͨͬͯ͠σʔλΛύʔε͢Δ
    ‣ ύʔεʹࣦഊͨ͠Β:clojure.spec/invalid͕ฦΔ
    user=> (s/conform (s/cat :i* (s/* integer?)
    :s* (s/* string?))
    ’(1 2 3 “foo” “bar”))
    {:i* [1 2 3], :s* ["foo" "bar"]}
    user=> (s/conform (s/cat :i* (s/* integer?)
    :s* (s/* string?))
    ’(1 2 “foo” 3 “bar”))

    View Slide

  50. DPOGPSN
    ‣ εϖοΫʹ͕ͨͬͯ͠σʔλΛύʔε͢Δ
    ‣ ύʔεʹࣦഊͨ͠Β:clojure.spec/invalid͕ฦΔ
    user=> (s/conform (s/cat :i* (s/* integer?)
    :s* (s/* string?))
    ’(1 2 3 “foo” “bar”))
    {:i* [1 2 3], :s* ["foo" "bar"]}
    user=> (s/conform (s/cat :i* (s/* integer?)
    :s* (s/* string?))
    ’(1 2 “foo” 3 “bar”))
    :clojure.spec/invalid
    user=>

    View Slide

  51. ζϯυίΩϤγͷεϖοΫ
    (ns zundoko
    (:require [clojure.spec :as s]))
    (s/def ::zun*4-doko
    (s/cat :1 ’#{ζϯ} :2 ’#{ζϯ} :3 ’#{ζϯ} :4 ’#{ζϯ} :5 ’#{υί}))
    (s/def ::has-no-zun*4-doko?
    (fn [xs]
    (every? #(not (s/valid? ::zun*4-doko %))
    (partition 5 1 xs))))
    (s/def ::zun-doko-kiyoshi
    (s/cat :preamble (s/& (s/* ’#{ζϯ υί}) ::has-no-zun*4-doko?)
    :zun*4-doko ::zun*4-doko
    :kiyoshi ’#{ΩϤγ}))

    View Slide

  52. ζϯυίΩϤγͷεϖοΫ
    user=> (s/conform ::zun-doko-kiyoshi
    ’(υί ζϯ υί
    ζϯ ζϯ ζϯ ζϯ υί ΩϤγ))

    View Slide

  53. ζϯυίΩϤγͷεϖοΫ
    user=> (s/conform ::zun-doko-kiyoshi
    ’(υί ζϯ υί
    ζϯ ζϯ ζϯ ζϯ υί ΩϤγ))
    {:preamble [υί ζϯ υί],
    :zun*4-doko {:1 ζϯ, :2 ζϯ, :3 ζϯ, :4 ζϯ, :5 υί},
    :kiyoshi ΩϤγ}
    user=> (s/explain ::zun-doko-kiyoshi
    ’(υί ζϯ υί
    ζϯ ζϯ ζϯ ζϯ υί))

    View Slide

  54. ζϯυίΩϤγͷεϖοΫ
    user=> (s/conform ::zun-doko-kiyoshi
    ’(υί ζϯ υί
    ζϯ ζϯ ζϯ ζϯ υί ΩϤγ))
    {:preamble [υί ζϯ υί],
    :zun*4-doko {:1 ζϯ, :2 ζϯ, :3 ζϯ, :4 ζϯ, :5 υί},
    :kiyoshi ΩϤγ}
    user=> (s/explain ::zun-doko-kiyoshi
    ’(υί ζϯ υί
    ζϯ ζϯ ζϯ ζϯ υί))
    val: () fails spec: :zundoko/zun-doko-kiyoshi predicate:
    (alt), Insufficient input
    nil
    user=>

    View Slide

  55. ϚΫϩͷߏจղੳ
    (s/def ::binding (s/cat :name simple-symbol? :init any?))
    (s/def ::bindings (s/and (s/* ::binding) vector?))
    (s/def ::with-open
    (s/cat :bindings ::bindings :body (s/* any?))
    (s/fdef with-open
    :args ::with-open
    :ret any?)
    user=> (s/conform ::with-open
    ’([in (open-file)] (slurp in)))
    {:bindings [{:name in, :init (open-file)}],
    :body [(slurp in)]}
    user=>

    View Slide

  56. ϚΫϩͷߏจղੳ
    (defmacro with-open [bindings & body]
    (let [[binding & more] (s/conform ::bindings bindings)]
    (if-not binding
    `(do ~@body)
    `(let ~[(:name binding) (:init binding)]
    (try
    (with-open ~(vec (s/unform ::bindings more))
    ~@body)
    (finally
    (.close ~(:name binding))))))))
    user=> (with-open [x] (slurp x)) ;;←ϚΫϩͷ࢖͍ํΛޡΔ
    CompilerException java.lang.IllegalArgumentException: Call to
    intro-to-spec.macros/with-open did not conform to spec:
    In: [0] val: () fails spec: :intro-to-spec.macros/bindings at:
    [:args :bindings :init] predicate: any?, Insufficient input
    :clojure.spec/args ([x] (slurp x))
    , compiling:(*cider-repl intro-to-spec*:1476:22)
    user=>

    View Slide

  57. ·ͱΊ
    ‣ DMPKVSFTQFD͸σʔλܕ΍ؔ਺ͷ࢓༷Λड़ޠͷ૊Έ
    ߹ΘͤͰهड़͢Δํ๏Λఏڙ͢Δ
    ‣ Ұ౓εϖοΫΛॻ͘ͱɺؔ਺ͷόϦσʔγϣϯ͚ͩ
    Ͱͳ͘ɺ3&1-Ͱͷ։ൃ࣌ͷαϯϓϧσʔλੜ੒΍
    ςετɺϚΫϩͷύʔε౳͍Ζ͍Ζ࢖͑Δ
    ‣ $MPKVSFʹ͓͚Δ։ൃͷ࢓ํ͕େ͖͘มΘΔՄೳੑ
    ͷ͋Δػೳ

    View Slide

  58. ݱঢ়͸ʜ
    ‣ ·ͩBMQIBϦϦʔεͰ"1*͕มΘΓ·ͬͯ͘Δஈ֊
    $MPKVSF4DSJQUͱͷ଍ฒΈ΋ଗͬͯͳ͍
    ͷͰ࣮ઓ
    ౤ೖ͸ΘΓͱݫ͍͠
    ௥هɿݱঢ়Ͱ͸$MPKVSFBMQIBͱ
    $MPKVSF4DSJQUͰ"1*Ϩϕϧͷޓ׵ੑ͕͋Δ໛༷
    ‣ Τϥʔϝοηʔδ͸৘ใෆ଍ͷঢ়ଶ͸ղফ͞Ε͍ͯ
    Δ͕ɺݱঢ়Ͱ͸৘ใաଟͰҰݟͯ͠෼͔Γʹ͍͘
    ศརʹ࢖͑ΔΑ͏ʹͳΔʹ͸։ൃ؀ڥଆͷαϙʔτ΋ඞཁ

    View Slide

  59. ࢀߟจݙ
    ‣ DMPKVSFTQFD3BUJPOBMFBOE0WFSWJFX
    IUUQDMPKVSFPSHBCPVUTQFD
    ‣ TQFD(VJEF
    IUUQDMPKVSFPSHHVJEFTTQFD
    ‣ $PHOJDBTUDMPKVSFTQFDXJUI3JDI)JDLFZ
    IUUQCMPHDPHOJUFDUDPNDPHOJDBTU

    View Slide