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
July 29, 2016
Programming
3
2.3k
clojure.specの話
2016/07/29 Lisp meetup #42 の発表資料です。
OHTA Shogo
July 29, 2016
Tweet
Share
More Decks by OHTA Shogo
See All by OHTA Shogo
テンクーでのClojure活用事例
athos
0
350
軽量デバッグツールPostmortemの紹介.pdf
athos
1
200
Clojure 1.10 概要紹介
athos
3
650
やってみる!clojure.spec
athos
4
1k
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
2
360
Other Decks in Programming
See All in Programming
Ruby×iOSアプリ開発 ~共に歩んだエコシステムの物語~
temoki
0
320
もうちょっといいRubyプロファイラを作りたい (2025)
osyoyu
1
440
@Environment(\.keyPath)那么好我不允许你们不知道! / atEnvironment keyPath is so good and you should know it!
lovee
0
120
Amazon RDS 向けに提供されている MCP Server と仕組みを調べてみた/jawsug-okayama-2025-aurora-mcp
takahashiikki
1
110
Reading Rails 1.0 Source Code
okuramasafumi
0
230
2025 年のコーディングエージェントの現在地とエンジニアの仕事の変化について
azukiazusa1
24
12k
AIコーディングAgentとの向き合い方
eycjur
0
270
Azure SRE Agentで運用は楽になるのか?
kkamegawa
0
2.3k
概念モデル→論理モデルで気をつけていること
sunnyone
2
210
Namespace and Its Future
tagomoris
6
700
Performance for Conversion! 分散トレーシングでボトルネックを 特定せよ
inetand
0
860
1から理解するWeb Push
dora1998
7
1.9k
Featured
See All Featured
The Power of CSS Pseudo Elements
geoffreycrofte
77
6k
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
Raft: Consensus for Rubyists
vanstee
140
7.1k
Code Review Best Practice
trishagee
70
19k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
252
21k
The Pragmatic Product Professional
lauravandoore
36
6.9k
Become a Pro
speakerdeck
PRO
29
5.5k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
23
1.4k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
18
1.1k
Balancing Empowerment & Direction
lara
3
620
Transcript
DMPKVSFTQFDͷ -JTQNFFUVQ !BUIPT
ࣗݾհ ‣ 5XJUUFS!BUIPT ‣ χϟϯύεגࣜձࣾॴଐ ‣ $MPKVSFίϯτϦϏϡʔλ
DMPKVSFTQFD ‣ ࣍ظϦϦʔεͰͷಋೖ͕ਐΊΒΕ͍ͯΔ৽ػೳ ‣ ੩తܕͰͳ͘ɺड़ޠͷΈ߹ΘͤʹΑͬͯσʔλ ܕɾؔͷ༷Λهड़͢Δ ‣ Ұ༷ εϖοΫ Λॻ͚ҰཻͰԿ͓͍͍͠
υΩϡϝϯςʔγϣϯ ܖϓϩάϥϛϯά ϓϩύςΟϕʔεςετ ϚΫϩͷߏจνΣοΫ
DMPKVSFTQFDͷ͍ํ ‣ [org.clojure/clojure “1.9.0-alphaXX”] Λ:dependenciesʹՃ ‣ ࠷৽൛BMQIB ݱࡏ ‣
(require ‘[clojure.spec :as s])
όϦσʔλͱͯ͠ͷ DMPKVSFTQFD
ड़ޠ ‣ ड़ޠ CPPMΛฦؔ͢ ͦͷ··ͰεϖοΫͱͯ͠ ͑Δ user=> (s/valid? integer? 42)
ड़ޠ ‣ ड़ޠ CPPMΛฦؔ͢ ͦͷ··ͰεϖοΫͱͯ͠ ͑Δ user=> (s/valid? integer? 42)
true user=> (s/valid? integer? “foo”)
ड़ޠ ‣ ड़ޠ CPPMΛฦؔ͢ ͦͷ··ͰεϖοΫͱͯ͠ ͑Δ user=> (s/valid? integer? 42)
true user=> (s/valid? integer? “foo”) false user=>
WBMJE FYQMBJO ‣ valid?εϖοΫΛຬ͔ͨ͢Ͳ͏͔Λฦ͢ ‣ explainεϖοΫΛຬͨ͞ͳ͍ՕॴΛࢦఠ͢Δ user=> (s/valid? integer? “foo”)
false user=> (s/explain integer? “foo”)
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)
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=>
εϖοΫͷ߹ ‣ andorΛͬͯෳͷεϖοΫΛΈ߹ΘͤՄೳ user=> (s/valid? (s/and integer? even?) 0) true
user=> (s/valid? (s/and integer? even?) 1) false
εϖοΫͷ߹ ‣ andorΛͬͯෳͷεϖοΫΛΈ߹ΘͤՄೳ 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”)
εϖοΫͷ߹ ‣ andorΛͬͯෳͷεϖοΫΛΈ߹ΘͤՄೳ 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=>
εϖοΫʹ໊લΛ͚ͭΔ ‣ defͰεϖοΫʹ໊લΛ͚ͭΔ͜ͱ͕Ͱ͖Δ ‣ εϖοΫࣗମΛ࠶ར༻Ͱ͖Δ user=> (s/def ::answer-to-everything (fn [x]
(= x 42)) :user/answer-to-everything user=> (s/valid? ::answer-to-everything 43)
εϖοΫʹ໊લΛ͚ͭΔ ‣ 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)
εϖοΫʹ໊લΛ͚ͭΔ ‣ 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=>
ίϨΫγϣϯͷεϖοΫ ‣ coll-ofmap-ofͰίϨΫγϣϯͷཁૉ͕εϖοΫ Λຬ͍ͨͯ͠Δ͔νΣοΫͰ͖Δ user=> (s/valid? (s/coll-of integer?) [1 2
3])
ίϨΫγϣϯͷεϖοΫ ‣ coll-ofmap-ofͰίϨΫγϣϯͷཁૉ͕εϖοΫ Λຬ͍ͨͯ͠Δ͔νΣοΫͰ͖Δ user=> (s/valid? (s/coll-of integer?) [1 2
3]) true user=> (s/valid? (s/coll-of integer?) [1 :a])
ίϨΫγϣϯͷεϖοΫ ‣ 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})
ίϨΫγϣϯͷεϖοΫ ‣ 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”})
ίϨΫγϣϯͷεϖοΫ ‣ 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=>
ίϨΫγϣϯͷεϖοΫ ‣ ΩʔʹΑͬͯͷܕ͕ҧ͏ϚοϓͷεϖοΫఆٛՄೳ 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})
ίϨΫγϣϯͷεϖοΫ ‣ ΩʔʹΑͬͯͷܕ͕ҧ͏ϚοϓͷεϖοΫఆٛՄೳ 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”})
ίϨΫγϣϯͷεϖοΫ ‣ ΩʔʹΑͬͯͷܕ͕ҧ͏ϚοϓͷεϖοΫఆٛՄೳ 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=>
%C$πʔϧͱͯ͠ͷ DMPKVSFTQFD
ؔͷεϖοΫ ‣ 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))
JOTUSVNFOU ‣ ؔʹҾ͕εϖοΫΛຬ͍ͨͯ͠Δ͔ͷνΣοΫ ΛΦϯɾΦϑͰ͖Δ user=> (require ’[clojure.spec.test :as t]) nil
user=> (t/instrument)
JOTUSVNFOU ‣ ؔʹҾ͕εϖοΫΛຬ͍ͨͯ͠Δ͔ͷνΣοΫ ΛΦϯɾΦϑͰ͖Δ user=> (require ’[clojure.spec.test :as t]) nil
user=> (t/instrument) [user/fizzbuzz] user=> (fizzbuzz 15)
JOTUSVNFOU ‣ ؔʹҾ͕εϖοΫΛຬ͍ͨͯ͠Δ͔ͷνΣοΫ ΛΦϯɾΦϑͰ͖Δ user=> (require ’[clojure.spec.test :as t]) nil
user=> (t/instrument) [user/fizzbuzz] user=> (fizzbuzz 15) “fizzbuzz” user=> (fizzbuzz “foo”)
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=>
DIFDL ‣ ҾͷεϖοΫΛຬͨ͢Λࣗಈੜͯ͠ɺͦͷΛؔʹ ͨ݁͠Ռ͕ΓͷεϖοΫΛຬ͔ͨ͢νΣοΫ͢Δ user=> (t/check)
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=>
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=> Ͱݺͼग़ͨ͠ͱ͖ʹ ࣦഊ͢Δ͜ͱΛݕग़
͕࣌ؒ͋Γͦ͏ͳΒ Ԡ༻ྫΛհ
δΣωϨʔλͱͯ͠ͷ DMPKVSFTQFD
HFO ‣ εϖοΫ͔ΒUFTUDIFDL༻ͷδΣωϨʔλΛ࡞Δ ‣ εϖοΫΛຬͨ͢ϥϯμϜͳΛੜͰ͖Δ ‣ 3&1-Ͱαϯϓϧσʔλ͕΄͍͠ͱ͖ʹศར user=> (require ’[clojure.test.check.generators
:as gen]) nil user=> (gen/generate (s/gen (s/coll-of integer?)))
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?)))
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=>
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)))))
ςετ࣮ߦ݁Ռ $ lein test
ςετ࣮ߦ݁Ռ $ 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. $
ύʔαͱͯ͠ͷ DMPKVSFTQFD
γʔέϯεͷεϖοΫ SFHFY ‣ SFHFYͰཁૉͷฒͼʹରͯ͠εϖοΫΛఆٛͰ͖Δ user=> (s/valid? (s/* integer?) ’(1 2
3)) true user=> (s/valid? (s/alt :i integer? :s string?) ’(“foo”))
γʔέϯεͷεϖοΫ 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”))
γʔέϯεͷεϖοΫ 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”))
γʔέϯεͷεϖοΫ 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=>
DPOGPSN ‣ εϖοΫʹ͕ͨͬͯ͠σʔλΛύʔε͢Δ ‣ ύʔεʹࣦഊͨ͠Β:clojure.spec/invalid͕ฦΔ user=> (s/conform (s/cat :i* (s/*
integer?) :s* (s/* string?)) ’(1 2 3 “foo” “bar”))
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”))
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=>
ζϯυίΩϤγͷεϖοΫ (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 ’#{ΩϤγ}))
ζϯυίΩϤγͷεϖοΫ user=> (s/conform ::zun-doko-kiyoshi ’(υί ζϯ υί ζϯ ζϯ ζϯ
ζϯ υί ΩϤγ))
ζϯυίΩϤγͷεϖοΫ user=> (s/conform ::zun-doko-kiyoshi ’(υί ζϯ υί ζϯ ζϯ ζϯ
ζϯ υί ΩϤγ)) {:preamble [υί ζϯ υί], :zun*4-doko {:1 ζϯ, :2 ζϯ, :3 ζϯ, :4 ζϯ, :5 υί}, :kiyoshi ΩϤγ} user=> (s/explain ::zun-doko-kiyoshi ’(υί ζϯ υί ζϯ ζϯ ζϯ ζϯ υί))
ζϯυίΩϤγͷεϖοΫ 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=>
ϚΫϩͷߏจղੳ (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=>
ϚΫϩͷߏจղੳ (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=>
·ͱΊ ‣ DMPKVSFTQFDσʔλܕؔͷ༷Λड़ޠͷΈ ߹ΘͤͰهड़͢Δํ๏Λఏڙ͢Δ ‣ ҰεϖοΫΛॻ͘ͱɺؔͷόϦσʔγϣϯ͚ͩ Ͱͳ͘ɺ3&1-Ͱͷ։ൃ࣌ͷαϯϓϧσʔλੜ ςετɺϚΫϩͷύʔε͍Ζ͍Ζ͑Δ ‣ $MPKVSFʹ͓͚Δ։ൃͷํ͕େ͖͘มΘΔՄೳੑ
ͷ͋Δػೳ
ݱঢ়ʜ ‣ ·ͩBMQIBϦϦʔεͰ"1*͕มΘΓ·ͬͯ͘Δஈ֊ $MPKVSF4DSJQUͱͷฒΈଗͬͯͳ͍ ͷͰ࣮ઓ ೖΘΓͱݫ͍͠ هɿݱঢ়Ͱ$MPKVSFBMQIBͱ $MPKVSF4DSJQUͰ"1*Ϩϕϧͷޓੑ͕͋Δ༷ ‣
Τϥʔϝοηʔδใෆͷঢ়ଶղফ͞Ε͍ͯ Δ͕ɺݱঢ়ͰใաଟͰҰݟ͔ͯ͠Γʹ͍͘ ศརʹ͑ΔΑ͏ʹͳΔʹ։ൃڥଆͷαϙʔτඞཁ
ࢀߟจݙ ‣ DMPKVSFTQFD3BUJPOBMFBOE0WFSWJFX IUUQDMPKVSFPSHBCPVUTQFD ‣ TQFD(VJEF IUUQDMPKVSFPSHHVJEFTTQFD ‣ $PHOJDBTUDMPKVSFTQFDXJUI3JDI)JDLFZ IUUQCMPHDPHOJUFDUDPNDPHOJDBTU