Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
やってみる!clojure.spec
Search
OHTA Shogo
March 14, 2018
Programming
4
1k
やってみる!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
テンクーでのClojure活用事例
athos
0
360
軽量デバッグツールPostmortemの紹介.pdf
athos
1
200
Clojure 1.10 概要紹介
athos
3
660
kitchen-async: a promising (?) Promise library, or a poor man's core.async
athos
3
490
Clojure 1.9 概要紹介
athos
4
1.5k
ここ最近のClojureScript
athos
5
1.7k
(= ? (+ nREPL Docker))
athos
0
540
clojure.specの話
athos
3
2.3k
clojure.specの話(仮)
athos
2
360
Other Decks in Programming
See All in Programming
Web フロントエンドエンジニアに開かれる AI Agent プロダクト開発 - Vercel AI SDK を観察して AI Agent と仲良くなろう! #FEC余熱NIGHT
izumin5210
3
370
Go言語の特性を活かした公式MCP SDKの設計
hond0413
1
130
Advance Your Career with Open Source
ivargrimstad
0
300
CSC305 Lecture 02
javiergs
PRO
1
260
dynamic!
moro
9
6.1k
あなたの知らない「動画広告」の世界 - iOSDC Japan 2025
ukitaka
0
360
GraphQL×Railsアプリのデータベース負荷分散 - 月間3,000万人利用サービスを無停止で
koxya
1
1k
ポスターセッション: 「まっすぐ行って、右!」って言ってラズパイカーを動かしたい 〜生成AI × Raspberry Pi Pico × Gradioの試作メモ〜
komofr
0
930
AIで開発生産性を上げる個人とチームの取り組み
taniigo
0
130
10年もののAPIサーバーにおけるCI/CDの改善の奮闘
mbook
0
750
Local Peer-to-Peer APIはどのように使われていくのか?
hal_spidernight
2
440
XP, Testing and ninja testing ZOZ5
m_seki
2
270
Featured
See All Featured
Git: the NoSQL Database
bkeepers
PRO
431
66k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.2k
Building Flexible Design Systems
yeseniaperezcruz
329
39k
Thoughts on Productivity
jonyablonski
70
4.9k
Gamification - CAS2011
davidbonilla
81
5.5k
The Art of Programming - Codeland 2020
erikaheidi
56
14k
Java REST API Framework Comparison - PWX 2021
mraible
33
8.8k
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
Agile that works and the tools we love
rasmusluckow
331
21k
Embracing the Ebb and Flow
colly
88
4.8k
What’s in a name? Adding method to the madness
productmarketing
PRO
23
3.7k
How to Ace a Technical Interview
jacobian
280
23k
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