A beginner-friendly REPL that combines
| (require '[clojure.core.specs.alpha]) | |
| (require '[clojure.spec.alpha :as s]) | |
| (require '[clojure.spec.test.alpha :as stest]) | |
| (require '[expound.alpha :as expound]) | |
| (set! s/*explain-out* (expound/custom-printer {:print-specs? false})) ; The "relevant specs" are very long, so let's omit them | |
| (stest/instrument) | |
| (let [foo/bar 42]) | |
| ;; CompilerException clojure.lang.ExceptionInfo: Call to clojure.core/let did not conform to spec: |
| (require '[clojure.spec.alpha :as s]) | |
| (require '[expound.alpha :as expound]) | |
| ;; For a cat spec... | |
| (s/def :ex/my-spec (s/cat | |
| :conn any? | |
| :x int? | |
| :y int?)) | |
| ;; with the default printer, valid values won't be printed ... |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>Try klipse</title> | |
| <link rel="stylesheet" type="text/css" href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css"> | |
| </head> | |
| <body> | |
| <pre class="language-klipse"> |
In an effort to gain at least a superficial understanding of the technical implementation of cryptocurrencies, I recently worked my way through "Learn Blockchains by Building One" using Clojure.
This was a good chance to experiment with using spec in new ways. At work, we primarily use spec to validate our global re-frame state and to validate data at system boundaries. For this project, I experimented with using instrumentation much more pervasively than I had done elsewhere.
This is not a guide to spec (there are already many excellent resources for this). Rather, it's an experience report exploring what went well, what is still missing, and quite a few unanswered questions for future research. If you have solutions for any of the problems I've presented, please let me know!
You don't need to know or care about blockchains to understand the code be
| user=> (require '[clojure.spec.alpha :as s]) | |
| nil | |
| user=> (require '[expound.alpha :as expound]) | |
| nil | |
| user=> (s/def ::name string?) | |
| :user/name | |
| user=> (s/def ::age pos-int?) | |
| :user/age | |
| user=> (s/def ::user (s/keys :req [::name] :req-un [::age])) | |
| :user/user |
| user=> (require '[expound.alpha :as expound]) | |
| nil | |
| user=> (require '[clojure.spec.test.alpha :as st]) | |
| nil | |
| user=> (require '[clojure.spec.alpha :as s]) | |
| nil | |
| user=> (s/fdef foo :args (s/cat :int int?)) | |
| user/foo | |
| user=> (defn foo [x] x) | |
| #'user/foo |
| ;; Problem: | |
| ;; When user clicks button, we must do async-effect-A, then async-effect-B. | |
| ;; We can't start B until A completes, because it needs the result of A. | |
| ;; We don't currently need to vary this sequence. We currently only need to | |
| ;; do [A, then B], not [A, then B] or [A, then C] | |
| ;;;;;; Implementation 1: Using a single re-frame effect | |
| ;;; in some component |
| ;; clojure 1.8.0 - lein repl | |
| user=> (defn hello "hello world") | |
| IllegalArgumentException Parameter declaration missing clojure.core/assert-valid-fdecl (core.clj:7181) | |
| ;; clojure 1.9.0-beta2 - lein repl | |
| user=> (defn hello "hello world") |
| (require '[clojure.spec.test.alpha :as st]) | |
| (require '[clojure.spec.alpha :as s]) | |
| (require '[expound.alpha :as expound]) | |
| (set! s/*explain-out* expound/printer) | |
| (st/instrument) | |
| (filter 123 "12333333333333") ; java.lang.Long cannot be cast to clojure.lang.IFn | |
| (s/fdef clojure.core/filter |