what is the literal simplest, shortest way to typecheck a clojure function. The regular ann form is pretty short:
(ann bar [Number -> Number])
(defn bar [b]
(+ 2 (foo b)))
But can we (with a macro or something) make it look smaller, like:
(defn bar [b : Number -> Number]
(+ 2 (foo b)))
Thanks for your advice!
I think Plumatic Schema is the best. See also this blog post.
Here is an example:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require [schema.core :as s]))
(s/defn add2 :- s/Num ; "superclass" for any number
[a :- s/Int ; must be an integer type
b :- Double] ; Must be a java.lang.Double
(+ a b))
(dotest
(throws? (add2 1 2))
(throws? (add2 1.0 2))
(is= 3.0 (add2 1 2.0)))
I also have some predefined "types" in addition to the basic ones. For example, tupelo.schema/Keymap is any map with keyword keys. Pair is any vector or sequence of length=2, etc.
Update
Please also see my Clojure template project. In particular,
the file test/clj/_bootstrap.clj exists for the sole purpose of enabling Plumatic Schema type checks when you type lein test (they are disabled by default, so there is no cost in production).
Strictly speaking, this is spec checking and not static typing, but you may want to check out Guardrails (a fork of Ghostwheel, which is unmaintained) which aims to tackle some of the problems static typing does and more:
(>defn ranged-rand
[start end]
[int? int? | #(< start end)
=> int? | #(>= % start) #(< % end)]
(+ start (long (rand (- end start)))))
Related
Given that :post takes a form that gets evaluated later (e.g. {:post [(= 10 %)]}). How could one dynamically pass a 'pre-made' vector of functions to :post?
For example:
(def my-post-validator
[prediate1 predicate2 predicate3])
(defn foo [x]
{:post my-post-validator}
x)
this throws a syntax error
Don't know how to create ISeq from: clojure.lang.Symbol
With my fuzzy understanding, it's because defn is a macro, and the thing that allows the % syntax in :post is that it's quoted internally..?
I thought maybe I then use a macro to pass a 'literal' of what I wanted evaluated
(defmacro my-post-cond [spec]
'[(assert spec %) (predicate2 %) (predicate n)])
example:
(defn foo [x]
{:post (my-post-cond :what/ever)}
x)
However, this attempt gives the error:
Can't take value of a macro
Is there a way to pass a vector of things to :post rather than having to define it inline?
You can't pass a vector of predefined predicates, but you can combine multiple predicates under a single name and use that name in :post:
(defn my-post-cond [spec val]
(and
;; Not sure if this is exactly what you want,
;; given that `val` becomes an assert message.
(assert spec val)
(predicate2 val)
;; You used `n` - I assume it was supposed to be `%`.
(predicate val)))
(defn foo [x]
{:post [(my-post-cond :what/ever %)]}
x)
I started off as a fan of pre- and post-conditions, but I've changed over the years.
For simple things, I prefer to use Plumatic Schema to not only test inputs & outputs, but to document them as well.
For more complicated tests & verifications, I just put in an explicit assert or similar. I also wrote a helper function in the Tupelo library to reduce repetition, etc when debugging or verifying return values:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn oddly
"Transforms its input. Throws if result is not odd"
[x]
(let [answer (-> x (* 3) (+ 2))]
(with-result answer
(newline)
(println :given x)
(assert (odd? answer))
(println :returning answer))))
(dotest
(is= 5 (oddly 1))
(throws? (oddly 2)))
with result
------------------------------------
Clojure 1.10.3 Java 11.0.11
------------------------------------
Testing tst.demo.core
:given 1
:returning 5
:given 2
Ran 2 tests containing 2 assertions.
0 failures, 0 errors.
Passed all tests
So with either the println or assert, the returned value is easy to see. If it fails the assert, an Exception is thrown as normal.
For the most part I understand what Clojure is telling me with it's error messages. But I am still clueless as to find out where the error happened.
Here is an example of what I mean
(defn extract [m]
(keys m))
(defn multiple [xs]
(map #(* 2 %) xs))
(defn process [xs]
(-> xs
(multiple) ; seq -> seq
(extract))) ; map -> seq ... fails
(process [1 2 3])
Statically typed languages would now tell me that I tried to pass a sequence to a function that expects a map on line X. And Clojure does this in a way:
ClassCastException java.lang.Long cannot be cast to java.util.Map$Entry
But I still have no idea where the error happened. Obviously for this instance it's easy because there are just 3 functions involved, you can easily just read through all of them but as programs grow bigger this gets old very quickly.
Is there a way find out where the errors happened other than just proof reading the code from top to bottom? (which is my current approach)
You can use clojure.spec. It is still in alpha, and there's still a bunch of tooling support coming (hopefully), but instrumenting functions works well.
(ns foo.core
(:require
;; For clojure 1.9.0-alpha16 and higher, it is called spec.alpha
[clojure.spec.alpha :as s]
[clojure.spec.test.alpha :as stest]))
;; Extract takes a map and returns a seq
(s/fdef extract
:args (s/cat :m map?)
:ret seq?)
(defn extract [m]
(keys m))
;; multiple takes a coll of numbers and returns a coll of numbers
(s/fdef multiple
:args (s/cat :xs (s/coll-of number?))
:ret (s/coll-of number?))
(defn multiple [xs]
(map #(* 2 %) xs))
(defn process [xs]
(-> xs
(multiple) ; seq -> seq
(extract))) ; map -> seq ... fails
;; This needs to come after the definition of the specs,
;; but before the call to process.
;; This is something I imagine can be handled automatically
;; by tooling at some point.
(stest/instrument)
;; The println is to force evaluation.
;; If not it wouldn't run because it's lazy and
;; not used for anything.
(println (process [1 2 3]))
Running this file prints (among other info):
Call to #'foo.core/extract did not conform to spec: In: [0] val: (2
4 6) fails at: [:args :m] predicate: map? :clojure.spec.alpha/spec
#object[clojure.spec.alpha$regex_spec_impl$reify__1200 0x2b935f0d
"clojure.spec.alpha$regex_spec_impl$reify__1200#2b935f0d"]
:clojure.spec.alpha/value ((2 4 6)) :clojure.spec.alpha/args ((2 4
6)) :clojure.spec.alpha/failure :instrument
:clojure.spec.test.alpha/caller {:file "core.clj", :line 29,
:var-scope foo.core/process}
Which can be read as: A call to exctract failed because the value passed in (2 4 6) failed the predicate map?. That call happened in the file "core.clj" at line 29.
A caveat that trips people up is that instrument only checks function arguments and not return values. This is a (strange if you ask me) design decision from Rich Hickey. There's a library for that, though.
If you have a REPL session you can print a stack trace:
(clojure.stacktrace/print-stack-trace *e 30)
See http://puredanger.github.io/tech.puredanger.com/2010/02/17/clojure-stack-trace-repl/ for various different ways of printing the stack trace. You will need to have a dependency such as this in your project.clj:
[org.clojure/tools.namespace "0.2.11"]
I didn't get a stack trace using the above method, however just typing *e at the REPL will give you all the available information about the error, which to be honest didn't seem very helpful.
For the rare cases where the stack trace is not helpful I usually debug using a call to a function that returns the single argument it is given, yet has the side effect of printing that argument. I happen to call this function probe. In your case it can be put at multiple places in the threading macro.
Re-typing your example I have:
(defn extract [m]
(keys m))
(defn multiply [xs]
(mapv #(* 2 %) xs))
(defn process [xs]
(-> xs
(multiply) ; seq -> seq
(extract))) ; map -> seq ... fails ***line 21***
(println (process [1 2 3]))
;=> java.lang.ClassCastException: java.lang.Long cannot be cast
to java.util.Map$Entry, compiling:(tst/clj/core.clj:21:21)
So we get a good clue in the exception where is says the file and line/col number tst.clj.core.clj:21:21 that the extract method is the problem.
Another indispensible tool I use is Plumatic Schema to inject "gradual" type checking into clojure. The code becomes:
(ns tst.clj.core
(:use clj.core tupelo.test)
(:require
[tupelo.core :as t]
[tupelo.schema :as tsk]
[schema.core :as s]))
(t/refer-tupelo)
(t/print-versions)
(s/defn extract :- [s/Any]
[m :- tsk/Map]
(keys m))
(s/defn multiply :- [s/Num]
[xs :- [s/Num]]
(mapv #(* 2 %) xs))
(s/defn process :- s/Any
[xs :- [s/Num]]
(-> xs
(multiply) ; seq -> seq
(extract))) ; map -> seq ... fails
(println (process [1 2 3]))
clojure.lang.ExceptionInfo: Input to extract does not match schema:
[(named (not (map? [2 4 6])) m)] {:type :schema.core/error, :schema [#schema.core.One{:schema {Any Any},
:optional? false, :name m}],
:value [[2 4 6]], :error [(named (not (map? [2 4 6])) m)]},
compiling:(tst/clj/core.clj:23:17)
So, while the format of the error message is a bit lengthy, it tells right away that we passed a parameter of the wrong type and/or shape into the method extract.
Note that you need a line like this:
(s/set-fn-validation! true) ; enforce fn schemas
I create a special file test/tst/clj/_bootstrap.clj so it is always in the same place.
For more information on Plumatic Schema please see:
https://github.com/plumatic/schema
https://youtu.be/o_jtwIs2Ot8
https://github.com/plumatic/schema/wiki/Basics-Examples
https://github.com/plumatic/schema/wiki/Defining-New-Schema-Types-1.0
Leonardo Borges has put together a fantastic presentation on Monads in Clojure. In it he describes the reader monad in Clojure using the following code:
;; Reader Monad
(def reader-m
{:return (fn [a]
(fn [_] a))
:bind (fn [m k]
(fn [r]
((k (m r)) r)))})
(defn ask [] identity)
(defn asks [f]
(fn [env]
(f env)))
(defn connect-to-db []
(do-m reader-m
[db-uri (asks :db-uri)]
(prn (format "Connected to db at %s" db-uri))))
(defn connect-to-api []
(do-m reader-m
[api-key (asks :api-key)
env (ask)]
(prn (format "Connected to api with key %s" api-key))))
(defn run-app []
(do-m reader-m
[_ (connect-to-db)
_ (connect-to-api)]
(prn "Done.")))
((run-app) {:db-uri "user:passwd#host/dbname" :api-key "AF167"})
;; "Connected to db at user:passwd#host/dbname"
;; "Connected to api with key AF167"
;; "Done."
The benefit of this is that you're reading values from the environment in a purely functional way.
But this approach looks very similar to the partial function in Clojure. Consider the following code:
user=> (def hundred-times (partial * 100))
#'user/hundred-times
user=> (hundred-times 5)
500
user=> (hundred-times 4 5 6)
12000
My question is: What is the difference between the reader monad and a partial function in Clojure?
The reader monad is a set of rules we can apply to cleanly compose readers. You could use partial to make a reader, but it doesn't really give us a way to put them together.
For example, say you wanted a reader that doubled the value it read. You might use partial to define it:
(def doubler
(partial * 2))
You might also want a reader that added one to whatever value it read:
(def plus-oner
(partial + 1))
Now, suppose you wanted to combine these guys in a single reader that adds their results. You'll probably end up with something like this:
(defn super-reader
[env]
(let [x (doubler env)
y (plus-oner env)]
(+ x y)))
Notice that you have to explicitly forward the environment to those readers. Total bummer, right? Using the rules provided by the reader monad, we can get much cleaner composition:
(def super-reader
(do-m reader-m
[x doubler
y plus-oner]
(+ x y)))
You can use partial to "do" the reader monad. Turn let into a do-reader by doing syntactic transformation on let with partial application of the environment on the right-hand side.
(defmacro do-reader
[bindings & body]
(let [env (gensym 'env_)
partial-env (fn [f] (list `(partial ~f ~env)))
bindings* (mapv #(%1 %2) (cycle [identity partial-env]) bindings)]
`(fn [~env] (let ~bindings* ~#body))))
Then do-reader is to the reader monad as let is to the identity monad (relationship discussed here).
Indeed, since only the "do notation" application of the reader monad was used in Beyamor's answer to your reader monad in Clojure question, the same examples will work as is with m/domonad Reader replaced with do-reader as above.
But, for the sake of variety I'll modify the first example to be just a bit more Clojurish with the environment map and take advantage of the fact that keywords can act as functions.
(def sample-bindings {:count 3, :one 1, :b 2})
(def ask identity)
(def calc-is-count-correct?
(do-reader [binding-count :count
bindings ask]
(= binding-count (count bindings))))
(calc-is-count-correct? sample-bindings)
;=> true
Second example
(defn local [modify reader] (comp reader modify))
(def calc-content-len
(do-reader [content ask]
(count content)))
(def calc-modified-content-len
(local #(str "Prefix " %) calc-content-len))
(calc-content-len "12345")
;=> 5
(calc-modified-content-len "12345")
;=> 12
Note since we built on let, we still have destructing at our disposal. Silly example:
(def example1
(do-reader [a :foo
b :bar]
(+ a b)))
(example1 {:foo 2 :bar 40 :baz 800})
;=> 42
(def example2
(do-reader [[a b] (juxt :foo :bar)]
(+ a b)))
(example2 {:foo 2 :bar 40 :baz 800})
;=> 42
So, in Clojure, you can indeed get the functionality of the do notation of reader monad without introducing monads proper. Analagous to doing a ReaderT transform on the identity monad, we can do a syntactic transformation on let. As you surmised, one way to do so is with partial application of the environment.
Perhaps more Clojurish would be to define a reader-> and reader->> to syntactically insert the environment as the second and last argument respectively. I'll leave those as an exercise for the reader for now.
One take-away from this is that while types and type-classes in Haskell have a lot of benefits and the monad structure is a useful idea, not having the constraints of the type system in Clojure allows us to treat data and programs in the same way and do arbitrary transformations to our programs to implement syntax and control as we see fit.
I'm going through project euler again to develop and hone my core.typed skills. However I am having a ton of difficulty understanding the output of the type checker. I'm asking after reading several introductory materials to core.typed as well as some information on the core.typed wiki.
I am using the following code, and please note that the annotation to apply, as well as mapping num over the input in function euler3 has no effect. The error message is still generated. Please ignore the obviously bad factors and prime? functions. I chose naive implementations to allow me to focus on the type annotations.
(ns euler-clj.euler3
"Project euler problem 3.
Uses a very naive prime tester, would use a sieve or probabilistic
prime algorithm for a larger problem set.
What is the largest prime factor of the number 600851475143?"
(:require [clojure.core.typed :refer
[ann check-ns Int fn> non-nil-return Seq cf]]
[euler-clj.euler1 :as e1]))
(non-nil-return clojure.lang.Numbers/quotient :all)
(ann ^:no-check clojure.core/apply [[Number Number * -> Number] (Seq Number) -> Number])
(ann factors [Int -> (Seq Int)])
(defn factors
[n]
(filter (fn> [m :- Int] (e1/div-by m n)) (range 1 (inc (quot n 2)))))
; Very bad prime function, should be sieve/probabilistic.
; Also identifies 1 as prime incorrectly.
(ann prime? [Int -> Boolean])
(defn prime? [n] (= [1] (factors n)))
(ann euler3 [Int -> Number])
(defn euler3
[n]
(apply max (map num (filter prime? (factors n)))))
(ann -main [-> nil])
(defn -main
[]
(prn (euler3 600851475143)))
This produces the following error:
Type Error (euler-clj.euler3:26:3) Bad arguments to apply:
Target: (Fn [java.lang.Number java.lang.Number * -> java.lang.Number])
Arguments: (clojure.core.typed/Seq java.lang.Number)
in: (clojure.core/apply clojure.core/max (clojure.core/map clojure.core/num (clojure.core/filter euler-clj.euler3/prime?
(euler-clj.euler3/factors n))))
ExceptionInfo Type Checker: Found 1 error clojure.core/ex-info (core.clj:4327)
Start collecting euler-clj.euler3
Start collecting euler-clj.euler1
Finished collecting euler-clj.euler1
Finished collecting euler-clj.euler3
Collected 2 namespaces in 62.584894 msecs
Start checking euler-clj.euler1
Checked euler-clj.euler1 in 165.76663 msecs
Start checking euler-clj.euler3
Checked euler-clj.euler3 in 200.997234 msecs
Checked 2 namespaces (approx. 60 lines) in 430.302357 msecs
What type annotations do I need to provide to get apply to accept max and then this seq of numbers?
Thanks
Firstly, you should not provide your own type for apply; it's implemented as a special case in the type checker.
In this case, core.typed needs a bit of convincing that max will never be called with zero arguments.
Here's one approach:
(ann euler3 [Int -> Number])
(defn euler3
[n]
(let [[f & r] (map num (filter prime? (factors n)))]
(if f
(apply max f r)
(throw (Exception. "No prime factors")))))
I'd like to implement some basic Physics/Chemistry formulas in Clojure.
I want to emphasize not on performance but on convenience, so the main
feature is type-checking. I was thinking that attaching meta to numbers
would accomplish the task.
For instance, this function:
(defn force [mass accel]
(* mass accel))
Should
Access the meta of first argument
Make sure it's of mass type, i.e. kilograms, grams etc. Throw an error if it's not.
Convert the numeric value to kilograms.
Do the same for acceleration.
Return the result with meta of Newtons.
I can overload * and other functions appropriately in my namespace.
The only problem is that it's impossible to attach meta to a Double.
What's a good way to get a something that behaves like a number, but can have metadata?
All this is much easier if you just create maps that contain both a number and a unit, rather than trying to smuggle the unit in as part of the number's metadata. After all, the unit is not conceptually bookkeeping data about the number: it is an integral part of the computation you are performing. And it's not as if you can ever use the number while ignoring its unit, so the ability to pass a decorated number into some "dumb" unit-unaware function such as + is not interesting either.
Given all this, it's easy to implement your force example function:
(defn force [{munit :unit :as mass} {aunit :unit :as accel}]
(assert (mass? munit))
(assert (accel? aunit))
{:unit :newton, :magnitude (* (:magnitude (to-kg mass))
(:magnitude (to-mss accel)))})
And of course if your to-kg and to-mss functions check the types themselves, you can omit them in force. Don't give up on the simplicity and transparency of maps for the imagined convenience of having numbers with metadata on them.
Here's an approach using gen-class. I just mocked the functions for checking and normalizing units. The only operation implemented is * which is used in force.
Please note that since the code is using gen-class and compile you'll need to save the following code to a file named big_decimal_meta.clj in the src folder of your leiningen project folder and then load it.
BigDecimalMeta using gen-class:
(ns big-decimal-meta
(:refer-clojure :exclude [* force])
(:gen-class
:name BigDecimalMeta
:extends java.math.BigDecimal
:state metadata
:init init
:implements [clojure.lang.IObj]))
(defn -init [& args]
[args (atom nil)])
(defn -withMeta [this metadata]
(reset! (.metadata this) metadata)
this)
(defn -meta [this]
(deref (.metadata this)))
(compile 'big-decimal-meta)
* and force functions with some example code:
(def x (with-meta (BigDecimalMeta. 1) {:unit :kg}))
(def y (with-meta (BigDecimalMeta. 3.5) {:unit :mss}))
(def z (with-meta (BigDecimalMeta. 4.5) {:unit :V}))
(defn unit [x]
(-> x meta :unit))
(defn * [x y]
(BigDecimalMeta. (str (.multiply x y))))
(defn mass? [x]
(#{:kg :gr :mg ,,,} (unit x)))
(defn accel? [x]
(#{:mss ,,,} (unit x)))
(defn to-kg [x] x)
(defn to-mss [x] x)
(defn force [mass accel]
(assert (mass? mass))
(assert (accel? accel))
(let [mass (to-kg mass)
accel (to-mss accel)]
(with-meta (* mass accel) {:unit :N})))
(println (force x y) (meta (force x y)))
(println (force x z) (meta (force x z)))