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

やってみる!clojure.spec

 やってみる!clojure.spec

2018/3/14のclj-nakano #5の資料です。

OHTA Shogo

March 14, 2018
Tweet

More Decks by OHTA Shogo

Other Decks in Programming

Transcript

  1. ࠓ೔ͷ಺༰ ‣ DMPKVSFTQFDೖ໳  എܠ  DMPKVSFTQFDͱ͸  TQFDͷجຊతͳ࢖͍ํ ‣

    DMPKVSFTQFDΛ΍ͬͯΈΔ  ؆୯ͳαϯϓϧϓϩδΣΫτͷதͰ࢖ͬͯΈ·͠ΐ͏
  2. DMPKVSFTQFDͱ͸ ‣ $MPKVSFͰಋೖ͞Εͨ৽ػೳ ‣ ड़ޠϕʔεͰσʔλܕ΍ؔ਺ͷ࢓༷Λهड़͠ɺͦΕΛνΣοΫ͢Δ࢓૊Έ  ੩తݕࠪͰ͸ͳ͘ɺܖ໿ϓϩάϥϛϯάʹ͍ۙ ‣ Ұ౓࢓༷ εϖοΫ

    Λॻ͚͹։ൃͷ༷ʑͳ৔໘Ͱར༻Մ  υΩϡϝϯςʔγϣϯ  όϦσʔγϣϯ  σʔλੜ੒  ϓϩύςΟϕʔεςετ  ϚΫϩͷߏจνΣοΫ
  3. ू߹΋εϖοΫ ‣ ू߹#{e1 … en}͸e1 ʜ en ͷ͍ͣΕ͔ͱ౳͍͠Α͏ͳ ஋Λද͢εϖοΫ ‣

    ڞ༻ମͷΑ͏ͳ஋Λදݱ͢Δͷʹศར (s/valid? #{0 1 2} 3) ;; => false (s/valid? #{0 1 2} 2) ;; => true
  4. ίϨΫγϣϯͷεϖοΫ ‣ s/coll-of ΍ s/map-ofͰίϨΫγϣϯͱͦͷཁૉ ͷεϖοΫΛද͢͜ͱ͕Ͱ͖Δ (s/def ::nums (s/coll-of int?))

    (s/def ::vals (s/map-of keyword? int?)) (s/valid? ::nums [3 1 4]) ;; => true (s/valid? ::vals {:a 0 :b 1}) ;; => true
  5. ϚοϓͷεϖοΫ ‣ s/keysͰΩʔʹΑΓ஋ͷܕ͕ҧ͏ϚοϓΛදݱͰ͖Δ ‣ :req-unͰඞਢͷΩʔɺ:opt-unͰΦϓγϣφϧͳΩʔΛࢦఆ ͢Δ (s/def ::id int?) (s/def

    ::name string?) (s/def ::gender #{:male :female}) (s/def ::person (s/keys :req-un [::id ::name] :opt-un [::gender]) (s/valid? ::person {:id 1 :name “athos” :gender :male}) ;; => true (s/valid? ::person {:id 1 :name “athos”}) ;; => true
  6. s/explain ‣ (s/explain <εϖοΫ> <஋>)  ஋͕εϖοΫΛຬ͍ͨͯ͠ͳ͍৔߹ʹɺͲ͕͜Ͳ͏ޡͬͯ ͍Δ͔Λࢦఠͯ͘͠ΕΔ  ग़ྗܗࣜ͸ΧελϚΠζͰ͖Δ

    (s/def ::id int?) (s/def ::name string?) (s/def ::person (s/keys :req-un [::id ::name])) (s/explain ::person {:id “42”}) ;; In: [:id] val: "42" fails spec: :user/id at: [:id] predicate: int? ;; val: {:id "42"} fails spec: :user/person predicate: (contains? % :name) ˡ:id͸int?ͩͱݴ͍ͬͯΔ ˡ:name͕ͳ͍ͱݴ͍ͬͯΔ
  7. s/gen ‣ (s/gen <εϖοΫ>)  εϖοΫΛຬͨ͢ϥϯμϜͳ஋Λੜ੒͢ΔδΣωϨʔλΛ ฦ͢  UFTUDIFDLHFOFSBUPST౳Λ࢖ͬͯϥϯμϜͳ஋Λੜ੒Ͱ͖Δ (require

    ‘[clojure.test.check.generators :as gen]) (s/gen (s/coll-of int?)) ;; => δΣωϨʔλ (gen/generate (s/gen (s/coll-of int?))) ;; => [-163 -1669986 346763815 11309055] (gen/generate (s/gen (s/coll-of int?))) ;; => [-116969371 -262061 95 10599611 36137714]
  8. st/instrument ‣ st/instrumentͰؔ਺ͷҾ਺͕εϖοΫΛຬ͍ͨͯ͠Δ͔ ͷνΣοΫ͕༗ޮʹͳΔ ‣ ։ൃ࣌ɾςετ࣌ʹ༗ޮʹ͓ͯ͘͠ͱศར (require ‘[clojure.spec.test.alpha :as st])

    (st/instrument `square) (square 3) ;; => 9 (square :foo) ;; ExceptionInfo Call to #'user/square did not conform to spec: ;; In: [0] val: :foo fails at: [:args :x] predicate: int? ;; clojure.core/ex-info (core.clj:4739)
  9. st/check ‣ Ҿ਺ͷεϖοΫ͔ΒϥϯμϜͳೖྗΛ࡞ͬͯؔ਺ʹ౉͠ɺ ໭Γ஋͕໭Γ஋ͷεϖοΫΛຬ͔ͨ͢Ͳ͏͔ΛνΣοΫ (st/check `square) ;; ({:spec #object[clojure.spec.alpha$fspec_impl$reify__2451 0x7caa6a11

    "clojure.spec.alpha$fspec_impl$reify__2451@7caa6a11" ], :clojure.spec.test.check/ret {:result #error { :cause "integer overflow" :via [{:type java.lang.ArithmeticException :message "integer overflow" :at [clojure.lang.Numbers throwIntOverflow "Numbers.java" 1526]}] …
  10. ՝୊ɿλεΫ؅ཧ ‣ ΠϯϝϞϦͰ3&1-͔Β࢖͏λεΫ؅ཧ ‣ IUUQTHJUIVCDPNBUIPTTQFDFYBNQMF ‣ ࢓༷  λεΫ͸*%ͱઆ໌ EFTDSJQUJPO

    ɺঢ়ଶ TUBUVT Λ࣋ͭ  λεΫϦετ͸ݸҎ্ͷλεΫΛอ࣋Ͱ͖Δ  λεΫϦετʹ*%Λࢦఆͯ͠λεΫΛऔಘͨ͠ΓɺλεΫͷઆ ໌΍ঢ়ଶΛมߋͨ͠ΓͰ͖Δ  λεΫͷঢ়ଶ͸QFOEJOH͔EPOFͷ͍ͣΕ͔ ॳظ͸QFOEJOH
  11. λεΫͷεϖοΫ (ns spec-example.todo (:require [clojure.spec.alpha :as s])) (s/def ::id nat-int?)

    (s/def ::description string?) (s/def ::status #{:pending :done}) (s/def ::task (s/keys :req-un [::id ::description ::status])) (s/def ::items (s/map-of ::id ::task)) (s/def ::task-list (s/keys :req-un [::items])) (def empty-task-list {:items {}})
  12. λεΫΛੜ੒ͯ͠ΈΔ (require ‘[clojure.test.check.generators :as gen]) (gen/generate (s/gen ::task)) ;; =>

    {:id 19861176, :description “bVRFEN89TC", :status :done} (gen/generate (s/gen ::task)) ;; => {:id 16, :description “n18x5AwDH0Ej0yyvyw1u8FLv", :status :done}
  13. λεΫϦετΛੜ੒ͯ͠ΈΔ (require ‘[clojure.test.check.generators :as gen]) (gen/generate (s/gen ::task-list)) ;; =>

    {:items {87201 {:id 1, :description "8J6aNkRHe1dPSc8mtp0AMKKg4", :status :pending}, 58 {:id 196, :description "juPASwWa55URSp21", :status :done}, 47466 {:id 3, :description "F3O9", :status :pending}, 5 {:id 37, :description "kTN7H180rUcqHg9sZ29699mJu14c", :status :done}, 61 {:id 2529932, :description "eLO00", :status :done}, 811 {:id 206, :description "3gyMilIqz0aw", :status :pending}, 35 {:id 28457067, :description "RRxYbrL38Uj42kR1MiJ2S8qMZ2t", :status :pending}}}
  14. add-task (defn add-task [tasks description] (let [id (count (:items tasks))

    task {:id id :description description :status :pending}] (assoc-in tasks [:items id] task))) (s/fdef add-task :args (s/cat :tasks ::task-list :description ::description) :ret ::task-list)
  15. add-taskΛinstrumentͯ͠ΈΔ (require ‘[clojure.spec.test.alpha :as st]) (st/instrument `add-task) (add-task empty-task-list “buy

    milk”) ;; => {:items {0 {:id 0, :description “buy milk”, :status :pending}}} (add-task empty-task-list :buy-milk) ;; ExceptionInfo Call to #'spec-example.todo/add-task did not conform to spec: ;; In: [1] val: :buy-milk fails spec: :spec-example.todo/description at: [:args :description] predicate: string? ;; clojure.core/ex-info (core.clj:4739)
  16. add-taskΛcheckͯ͠ΈΔ (st/check `add-task) ;; => ({:spec #object[clojure.spec.alpha$fspec_impl$reify __2451 0x5e62054e “clojure.spec.alpha$fspec_impl$reify__2451@

    5e62054e"], :clojure.spec.test.check/ret {:result true, :num-tests 1000, :seed 1521012250499}, :sym spec-example.todo/add-task})