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
330
軽量デバッグツールPostmortemの紹介.pdf
athos
1
200
Clojure 1.10 概要紹介
athos
3
640
やってみる!clojure.spec
athos
4
1k
kitchen-async: a promising (?) Promise library, or a poor man's core.async
athos
3
470
Clojure 1.9 概要紹介
athos
4
1.4k
ここ最近のClojureScript
athos
5
1.7k
(= ? (+ nREPL Docker))
athos
0
530
clojure.specの話(仮)
athos
2
350
Other Decks in Programming
See All in Programming
Носок на сок
bo0om
0
1.5k
Instrumentsを使用した アプリのパフォーマンス向上方法
hinakko
0
260
Global Azure 2025 @ Kansai / Hyperlight
kosmosebi
0
170
In geheimer Mission: AI Agents entwickeln
joergneumann
0
130
Browser and UI #2 HTML/ARIA
ken7253
2
190
SpringBootにおけるオブザーバビリティのなにか
irof
0
260
M5UnitUnified 最新動向 2025/05
gob
0
150
開発者フレンドリーで顧客も満足?Platformの秘密
algoartis
0
250
CRUD から CQRS へ ~ 分離が可能にする柔軟性
tkawae
0
180
クラシルリワードにおける iOSアプリ開発の取り組み
funzin
1
230
“技術カンファレンスで何か変わる?” ──RubyKaigi後の自分とチームを振り返る
ssagara00
0
170
Embracing Ruby magic
vinistock
2
300
Featured
See All Featured
Building a Scalable Design System with Sketch
lauravandoore
462
33k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
19
1.2k
Building an army of robots
kneath
305
45k
The Invisible Side of Design
smashingmag
299
50k
Making the Leap to Tech Lead
cromwellryan
133
9.3k
Art, The Web, and Tiny UX
lynnandtonic
298
21k
Six Lessons from altMBA
skipperchong
28
3.8k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
227
22k
Fontdeck: Realign not Redesign
paulrobertlloyd
84
5.5k
Fantastic passwords and where to find them - at NoRuKo
philnash
51
3.2k
Producing Creativity
orderedlist
PRO
344
40k
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