Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
やってみる!clojure.spec
OHTA Shogo
March 14, 2018
Programming
4
620
やってみる!clojure.spec
2018/3/14のclj-nakano #5の資料です。
OHTA Shogo
March 14, 2018
Tweet
Share
More Decks by OHTA Shogo
See All by OHTA Shogo
軽量デバッグツールPostmortemの紹介.pdf
athos
1
160
Clojure 1.10 概要紹介
athos
3
560
kitchen-async: a promising (?) Promise library, or a poor man's core.async
athos
3
260
Clojure 1.9 概要紹介
athos
4
1.1k
ここ最近のClojureScript
athos
5
1.3k
(= ? (+ nREPL Docker))
athos
0
420
clojure.specの話
athos
3
1.9k
clojure.specの話(仮)
athos
2
310
Clojureの基礎 〜マクロ編〜
athos
2
570
Other Decks in Programming
See All in Programming
Explore Java 17 and beyond
josepaumard
3
640
WindowsコンテナDojo:第2回 Windowsコンテナアプリのビルド、公開、デプロイ
oniak3ibm
PRO
0
140
확장 가능한 테라폼 코드 관리 (Scalable Terraform Code Management)
posquit0
1
310
アプリのログをチーム外で活用してもらうためにやったこと
shotakashihara
0
150
脱オブジェクト指向講座(5分LT資料)
kishida
8
11k
microCMS × Shopifyで、ECサイトがリニューアル後急成長した話
microcms
0
450
Nix for Scala folks
kubukoz
0
120
From Java 11 to 17 and beyond
josepaumard
0
290
近況PHP / PHP in now a days
uzulla
4
1.5k
Android入門
hn410
0
300
もしも、 上司に鬼退治を命じられたら~プロジェクト計画編~
higuuu
0
270
2022 Android Training
mixi_engineers
1
660
Featured
See All Featured
Creatively Recalculating Your Daily Design Routine
revolveconf
205
10k
Art, The Web, and Tiny UX
lynnandtonic
280
17k
The World Runs on Bad Software
bkeepers
PRO
56
5.2k
Pencils Down: Stop Designing & Start Developing
hursman
112
9.8k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
3
430
GraphQLとの向き合い方2022年版
quramy
16
8.1k
10 Git Anti Patterns You Should be Aware of
lemiorhan
638
52k
Automating Front-end Workflow
addyosmani
1351
200k
Six Lessons from altMBA
skipperchong
14
1.3k
The Straight Up "How To Draw Better" Workshop
denniskardys
225
120k
Three Pipe Problems
jasonvnalue
89
8.6k
The Invisible Side of Design
smashingmag
289
48k
Transcript
ͬͯΈΔʂDMPKVSFTQFD DMKOBLBOP !BUIPT
ࣗݾհ ‣ 5XJUUFS!BUIPT ‣ (JU)VCBUIPT ‣ χϟϯύεגࣜձࣾॴଐ ‣ $MPKVSFίϯτϦϏϡʔλ
ࠓͷ༰ ‣ DMPKVSFTQFDೖ എܠ DMPKVSFTQFDͱ TQFDͷجຊతͳ͍ํ ‣
DMPKVSFTQFDΛͬͯΈΔ ؆୯ͳαϯϓϧϓϩδΣΫτͷதͰͬͯΈ·͠ΐ͏
DMPKVSFTQFDೖ
എܠɿ$MPKVSFͷ՝ ‣ ؔΛͲ͏ͬͯ͏ͷ͔͔Γʹ͍͘ʜ ҾʹԿΛड͚औΔͷ͔ɺΓͱͯ͠ԿΛฦ͢ͷ͔ $MPKVSFͰͨͩͷϚοϓϕΫλΛଟ༻͢ΔͷͰɺɹ Ͳ͏͍͏͕ظ͞Ε͍ͯΔͷ͔ผ͠ʹ͍͘ ‣ Τϥʔϝοηʔδ͕͔Γʹ͍͘ʜ
$MPKVSFͷؔجຊతʹ(BSCBHF*O(BSCBHF0VU ޡͬͨೖྗʹରͯ͠ɺ+7.ͷΤϥʔ͕ͦͷ··ग़Δ ͜ͱଟ͍
DMPKVSFTQFDͱ ‣ $MPKVSFͰಋೖ͞Εͨ৽ػೳ ‣ ड़ޠϕʔεͰσʔλܕؔͷ༷Λهड़͠ɺͦΕΛνΣοΫ͢ΔΈ ੩తݕࠪͰͳ͘ɺܖϓϩάϥϛϯάʹ͍ۙ ‣ Ұ༷ εϖοΫ
Λॻ͚։ൃͷ༷ʑͳ໘Ͱར༻Մ υΩϡϝϯςʔγϣϯ όϦσʔγϣϯ σʔλੜ ϓϩύςΟϕʔεςετ ϚΫϩͷߏจνΣοΫ
s/valid? ‣ (s/valid? <εϖοΫ> <>) ͕εϖοΫΛຬ͍ͨͯ͠Δ͔ΛνΣοΫ͢Δ (require ‘[clojure.spec.alpha :as
s]) (s/valid? int? 42) ;; => true (s/valid? int? :foo) ;; => false
ड़ޠεϖοΫʹͳΔ ‣ ҙͷड़ޠ ਅِΛฦؔ͢ ΛεϖοΫͱͯ͑͠Δ ‣ ࠐΈ͚ؔͩͰͳ͘ϢʔβఆٛؔͰແ໊ؔͰՄ (s/valid? string? “foo”)
;; => true (s/valid? #(> % 10) 11) ;; => true
εϖοΫʹ໊લΛ͚ͭΔ ‣ s/defͰεϖοΫʹ໊લΛ͚ͭΔ͜ͱ͕Ͱ͖Δ ‣ εϖοΫΛ࠶ར༻Ͱ͖Δ (s/def ::id int?) (s/def :person/name
string?) (s/valid? ::id 42) ;; => true (s/valid? :person/name “Rich Hickey”) ;; => true
ू߹εϖοΫ ‣ ू߹#{e1 … en}e1 ʜ en ͷ͍ͣΕ͔ͱ͍͠Α͏ͳ Λද͢εϖοΫ ‣
ڞ༻ମͷΑ͏ͳΛදݱ͢Δͷʹศར (s/valid? #{0 1 2} 3) ;; => false (s/valid? #{0 1 2} 2) ;; => true
ίϨΫγϣϯͷεϖοΫ ‣ 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
ϚοϓͷεϖοΫ ‣ 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
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) ˡ:idint?ͩͱݴ͍ͬͯΔ ˡ:name͕ͳ͍ͱݴ͍ͬͯΔ
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]
ؔͷεϖοΫ ‣ ؔͷҾ͓ΑͼΓͷεϖοΫΛهड़Ͱ͖Δ ‣ :fnͰҾͱΓͷؒͷؔʹ͍ͭͯنఆͰ͖Δ (defn square [x] (* x
x)) (s/fdef square :args (s/cat :x int?) :ret int?)
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)
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]}] …
ࢀߟɿsquareͷగਖ਼൛ ‣ *MPOHͷൣғͷಉ࢜ͷֻ͚ࢉΦʔόʔϑϩʔ͢Δ Մೳੑ͕͋ΔͷͰΘΓʹ*’Λ͏ ‣ #JH*OUJOU Ͱͳ͍ͷͰJOUFHFS Λ͏ (defn square
[x] (*’ x x)) (s/fdef square :args (s/cat :x int?) :ret integer?)
DMPKVSFTQFDΛͬͯΈΔ
՝ɿλεΫཧ ‣ ΠϯϝϞϦͰ3&1-͔Β͏λεΫཧ ‣ IUUQTHJUIVCDPNBUIPTTQFDFYBNQMF ‣ ༷ λεΫ*%ͱઆ໌ EFTDSJQUJPO
ɺঢ়ଶ TUBUVT Λ࣋ͭ λεΫϦετݸҎ্ͷλεΫΛอ࣋Ͱ͖Δ λεΫϦετʹ*%Λࢦఆͯ͠λεΫΛऔಘͨ͠ΓɺλεΫͷઆ ໌ঢ়ଶΛมߋͨ͠ΓͰ͖Δ λεΫͷঢ়ଶQFOEJOH͔EPOFͷ͍ͣΕ͔ ॳظQFOEJOH
͡Ί͔ͨ ‣ 3&1-Λىಈ ‣ ͘͠ ‣ 3&1-ͰҎԼΛೖྗ $ clj -Adev
$ lein repl (goto ‘spec-example.todo)
λεΫͷεϖοΫ (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 {}})
λεΫΛੜͯ͠ΈΔ (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}
λεΫϦετΛੜͯ͠ΈΔ (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}}}
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)
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)
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})
checkΛ௨ͯ͠ΈΔ ‣ count-tasksͱall-tasksʹਖ਼͍͠εϖοΫΛఆٛ͠ ͯɺcheckΛ௨ͯ͠Έ·͠ΐ͏ (defn count-tasks [tasks] (count (:items tasks)))
(defn all-tasks [tasks] (sequence (vals (:items tasks))))
checkΛ௨ͯ͠ΈΔ ‣ count-tasksͱall-tasksʹਖ਼͍͠εϖοΫΛఆٛ͠ ͯɺcheckΛ௨ͯ͠Έ·͠ΐ͏ (s/fdef count-tasks :args (s/cat :tasks ::task-list)
:ret int?) (s/fdef all-tasks :args (s/cat :tasks ::task-list) :ret (s/coll-of ::task))
͕࣌ؒ͋Ε ϓϩύςΟϕʔεςετͷհ
·ͱΊ ‣ DMPKVSFTQFDͰσʔλؔͷεϖοΫΛఆٛ͢Δ͜ͱ Ͱɺ$MPKVSFͰΑΓݎ࿚ͳίʔυΛॻ͚ΔΑ͏ʹ ‣ ҰεϖοΫΛఆ͓ٛͯ͘͠ͱɺ։ൃ࣌ɾςετ࣌ͳͲ ༷ʑͳ໘ͰεϖοΫΛԠ༻͢Δ͜ͱ͕Ͱ͖Δ ‣ ͨͩ͠ɺ৽͔ͭ͘͠ಠಛͷػೳͳͨΊɺ·ͩ·ͩϕετ ϓϥΫςΟεݻ·͍ͬͯͳ͍෦ଟ͍
‣ ΈΜͳͰҰॹʹ͍͖ͬͯ·͠ΐ͏
͓͢͢ΊϥΠϒϥϦ ‣ 0SDIFTUSBinstrumentͰΓνΣοΫͯ͘͠ΕΔ IUUQTHJUIVCDPNKFBZFPSDIFTUSB ‣ UFTUDIVDLUFTUDIFDLΛ͏ͱ͖ͷϢʔςΟϦςΟू IUUQTHJUIVCDPNHGSFEFSJDLTUFTUDIVDL ‣ &YQPVOEFYQMBJOͷ݁ՌΛݟ͘͢ܗͯ͘͠ΕΔ IUUQTHJUIVCDPNCICFYQPVOE
‣ 1JOQPJOUFSಉ্ IUUQTHJUIVCDPNBUIPT1JOQPJOUFS
ϦϯΫू ‣ DMPKVSFTQFDͷ֓ཁ ຊޠʂ IUUQTKBQBODMPKVSJBOTHJUIVCJPDMPKVSFTJUFKBBCPVUTQFDIUNM ‣ DMPKVSFTQFDΨΠυ IUUQTDMPKVSFPSHHVJEFTTQFD ‣
1SPHSBNNJOH$MPKVSF SEFE IUUQTQSBHQSPHDPNCPPLTIDMPKQSPHSBNNJOHDMPKVSFUIJSEFEJUJPO