I want to get the clojure.core.typed type check passing, but I'm getting a type error that I don't understand.
My questions are:
- What does the error below mean?
- How can I fix it?
Here is my code (which I realize is incorrect):
(ns clj.util.map
(:require [clojure.core.typed :as t]))
(t/ann map-vals
(All [k v1 v2]
(Fn [ (Fn [(t/Option v1) -> (t/Option v2)])
(t/Option (t/Map k v1)) ->
(t/Option (t/Map k v2)) ])))
(defn map-vals
;; FIXME: Incorrect code
"Apply a function to each of the values in a map, returning the updated map."
[f hm]
(t/doseq> [k :- Any (keys hm)]
(assoc hm k (f (get hm k)))))
Here is the output of lein typed check clj.util.map:
Initializing core.typed ...
"Elapsed time: 6697.604 msecs"
core.typed initialized.
Start collecting clj.util.map
Finished collecting clj.util.map
Collected 1 namespaces in 6851.111 msecs
Start checking clj.util.map
Checked clj.util.map in 968.041 msecs
Checked 1 namespaces (approx. 21 lines) in 7823.552 msecs
Type Error (clj.util.map:14:23) Polymorphic function clojure.core/keys could not be applied to arguments:
Polymorphic Variables:
k
Domains:
(t/Map k Any)
Arguments:
(t/Option (t/Map k v1))
Ranges:
(t/Seq k) :object {:path [Keys], :id 0}
in: (clojure.core/keys hm)
in: (clojure.core/seq (clojure.core/keys hm))
Type Checker: Found 1 error
Found errors
Subprocess failed
The first part of the error tells you this concerns a call to
clojure.core/keys. You can lookup the type with(cf keys).(All [k] [(Map k Any) -> (Seq k) :object {:id 0 :path [Keys]}])The error is basically summarising this polymorphic type juxtaposed with the actual types provided to the function.
The
Polymorphic Variableslist all the variables in theAllbinder and their type bounds.kis the only variable, which has essentially no bounds, sokis displayed.The
Domainslist all the parameter types (to the left of the->) in order. If multiple arities were specified withFn, each parameter list would be displayed sequentially.The
Argumentsshow you what types are actually passed to the function. The code at 14:23 is presumably this call(keys hm), so the type of the first argument is displayed:(t/Option (t/Map k v1))The
Rangeslist all the return types (to the right of the->) in order.Sometimes there is an
Expectedtype, which must match up with aRangein the same way all theArgumentsmust match up with aDomain.We can diagnose this error by comparing the
Domainswith theArguments. The list ofArgumentsmust fit under one list ofDomains, and the match is attempted top-down. If noDomainsfit with theArguments, we get a type error like this; it's often too complicated to pinpoint exactly where the constraint algorithm failed, so the user is presented with a lot of information.In this case, we try and fit the argument
(t/Option (t/Map k v1))under the domain(t/Map k Any), which fails. This is because the argument(t/Option (t/Map k v1))is the same as(U nil (t/Map k v1)), andnildoes not fit under the domain(t/Map k Any).There are a couple of ways to fix this. You basically need to ensure
keysisn't passednil. Here's one option:(keys (or hm {}))