core-typed

2024-03-20T02:07:13.894629Z

❤️ 4
🎉 6
2024-03-20T17:27:01.455819Z

Is there a way to assert to the type checker that every map in a vector is of a certain HMap type? Like by calling a function to validate that or something? Kind of like the inference using (if (int? somevar) (do something with somevar as an int) (throw ...)). I am wondering if its possible to do something like (if (valid-myhmap-type? map ) (do something with myhmap type) (throw ...))

2024-03-20T18:17:58.074419Z

Oh great how do I reference a type I defined via defalias in cast?

2024-03-20T18:19:09.563109Z

Just like t/ann-form or t/inst.

2024-03-20T18:24:44.621839Z

Weird i'm getting no such var t/cast. These are the only functions/macros my repl can see.

2024-03-20T18:24:55.748109Z

it's in clojure.core.typed

2024-03-20T18:25:11.471009Z

Oh my bad let me see

2024-03-20T19:08:37.557719Z

It seems you cannot cast a map with a vector value. Like {:x t/int :xs (t/Vec t/Int)}. Trying to cast any map into that type gives an error Don't know how to apply type: (clojure.lang.IPersistentVector x)

2024-03-20T19:09:01.233079Z

But you can use pred on it which is interesting

2024-03-20T19:30:29.031899Z

Thanks I'll take a look.

👍 1
2024-03-20T19:41:27.502779Z

Use pred. cast is a based on an old idea that I've moved away from. It might wrap the value and check it lazily, which comes with its own set of issues.

2024-03-20T19:41:50.502449Z

pred will blow up at compile time if it cannot check it without wrapping it.

2024-03-20T19:43:24.740539Z

Even pred is not a great solution compared to schema/spec/malli validators. But it will do the job.

2024-03-20T19:45:09.428029Z

Would one of those validators allow the function to typecheck? And whats your reasoning for pred not being as great a solution?

2024-03-20T19:53:19.672459Z

Yes, if you do (if ((pred '{my hmap}) foo) good (throw)) that should infer correctly.

2024-03-20T19:56:13.814219Z

What pred has over other validators is that it refuses to create a predicate for a value it cannot check 100% deterministically. But on the downside, pred is a macro so it can generate a ton of code. It also ties my hands as an implementor because I can't lazily load the contract implementation, so everyone has to load the implementation of pred even if they don't use it.

2024-03-20T19:56:39.409299Z

Mostly minor details for users.

Prashant 2024-03-20T19:16:49.724049Z

I was trying out the commented https://github.com/typedclojure/typedclojure/blob/main/typed/lib.clojure/src/typed/ann/clojure.cljc#L2084 but kept on getting error: Annotation:

cc/empty
   (t/All [x empty-fn]
      [(IPersistentCollection t/Any :empty-fn empty-fn) :-> (empty-fn)]
      [nil :-> nil]
      [(t/U nil (IPersistentCollection t/Any :empty-fn empty-fn)) :-> (t/U nil (empty-fn))])
Error:
Start checking typed.core
Type Error (file:/src/typed/core.clj:20:27) 
Internal Error (file:/src/typed/core.clj:20:27) Bad All syntax: (t/All [x empty-fn] [(IPersistentCollection x :empty-fn empty-fn) :-> (empty-fn)] [nil :-> nil] [(t/U nil (IPersistentCollection x :empty-fn empty-fn)) :-> (t/U nil (empty-fn))])
Execution error (ExceptionInfo) at clojure.core.typed.errors/print-errors! (errors.cljc:299).
Type Checker: Found 1 error

2024-03-20T20:06:09.078779Z

Oh that's just a sketch. It should look similar to cc/conj. This sketch was trying to make it extensible.

2024-03-20T20:08:30.878849Z

More like:

(t/IFn [(t/Vec t/Any) :-> (t/Vec t/Nothing)]
                    [(t/Map t/Any t/Any) :-> (t/Map t/Nothing t/Nothing)]
                    [(t/Set t/Any) :-> (t/Set t/Nothing)]
                    [(t/ISeq t/Any) :-> (t/ISeq t/Nothing)]
                    ...)

2024-03-20T20:23:06.468529Z

I'll push an annotation to main once I test it.

1
Prashant 2024-03-20T20:37:06.913899Z

I found this https://github.com/typedclojure/typedclojure/blob/main/typed/lib.clojure/src/typed/ann/clojure.cljc#L2074very interesting

IPersistentCollection [[x :variance :covariant]
                        :conj-fn [conj-fn :kind (t/TFn [[x :variance :covariant]] (IPersistentCollection x))]
                        :empty-fn [empty-fn :kind (t/TFn [] (IPersistentCollection t/Nothing :count (ExactCount 0)))]]
I found this very generic and a great way to encode interface behaviour e.g. conj-fn and empty-fn in typedef. How can one use such a Type annotation? would it be via defalias or TFn?

2024-03-20T20:41:09.720909Z

Yes I agree except it's all just wishful thinking, I haven't actually implemented anything powerful enough to manage this kind of annotation. This comment is actually fleshed out a bit more https://github.com/typedclojure/typedclojure/blob/73fee72abafa47c6c0a285bd674f8952e0604bb3/typed/lib.clojure/src/typed/ann/clojure/jvm.clj#L40-L53.

2024-03-20T20:43:12.222189Z

There's even more sketches and ideas here if you're interested https://github.com/typedclojure/typedclojure/blob/main/typed/clj.checker/test/clojure/core/typed/test/conj_experiment.clj

Prashant 2024-03-20T20:44:11.313039Z

Thanks Ambrose. This is very interesting. I often go through typed.clojure code to fish out missed type aliases or types that went under the radar.

😛 1
Prashant 2024-03-20T20:45:07.480749Z

and the sketches for possible directions are very illuminating 🙂

2024-03-20T20:45:58.453939Z

Yeah! The idea of a once-and-for-all type for conj is very attractive. I think Scala can easily do this sort of thing. I've concentrated on other areas.

2024-03-20T20:48:36.782529Z

I haven't really considered how powerful it would be tbh, most of clojure's data types are build on conj. Might it change how we annotate cc/into?

Prashant 2024-03-20T20:55:36.330799Z

If the experiment succeeds, then the annotation for cc/into would probably change. I agree that Scala 3 is really capable in type driven development.

2024-03-20T21:02:44.264829Z

maybe:

cc/into
#_
(t/All [x y Conj]
       [(t/Coll x :conjable y :Conj Conj) (t/Transducer y x) (t/Seqable y) :-> (Conj x)])

2024-03-20T21:03:30.318439Z

Something a bit fishy about it. Maybe someone has already tried to encode the clojure hierarchy in scala. (Oh, should return (Conj x) I think.)

2024-03-20T21:11:40.878799Z

Hmm no still misses that in (into {} (map (fn [[k v]] ...) [{:a 1} [:a 1]]) the transducer will be passed [:a 1] .

2024-03-20T21:13:02.231099Z

Oh wait that's not what happens

typed.dev=> (into {} (map (fn [e] (prn e) e)) [{:a 1} [:a 2]])
{:a 1}
[:a 2]
{:a 2}

2024-03-20T21:15:31.900699Z

It's the return value of the transducer that's interesting not the input. It can return either an entry or a map. Yeah that's missing in the annotation.

2024-03-20T21:19:22.171949Z

well it doesn't work on lists for some reason, but here's the non-extensible annotation for empty https://github.com/typedclojure/typedclojure/commit/b296eaef32825549b7daebd680fbb22115fe11b6

👍 1