Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does this clojure.core.typed type error mean?

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
like image 916
Jeff Terrell Ph.D. Avatar asked Dec 11 '22 07:12

Jeff Terrell Ph.D.


1 Answers

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 Variables list all the variables in the All binder and their type bounds. k is the only variable, which has essentially no bounds, so k is displayed.

The Domains list all the parameter types (to the left of the ->) in order. If multiple arities were specified with Fn, each parameter list would be displayed sequentially.

The Arguments show 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 Ranges list all the return types (to the right of the ->) in order.

Sometimes there is an Expected type, which must match up with a Range in the same way all the Arguments must match up with a Domain.

We can diagnose this error by comparing the Domains with the Arguments. The list of Arguments must fit under one list of Domains, and the match is attempted top-down. If no Domains fit with the Arguments, 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)), and nil does not fit under the domain (t/Map k Any).

There are a couple of ways to fix this. You basically need to ensure keys isn't passed nil. Here's one option:

(keys (or hm {}))

like image 189
Ambrose Avatar answered Jan 14 '23 14:01

Ambrose