core-typed

Corneliu Hoffman 2023-10-10T07:52:24.367919Z

Encouraged by the amazing promptness of the answer tgo my first question I persist with 2 more 1. Is there a way to have parametric defaliases? For example to alias (t/List a) as (MyList a) (perhaps even with bounds on a)? 2. How far can one go with type-level manipulation? I am enviously looking at things like this in Haskell

:: forall fs rs rs2 rs2'. (fs ⊆ rs, fs ⊆ rs2, rs ⊆ (rs ++ rs2'), rs2' ⊆ rs2, rs2' ~ RDeleteAll fs rs2, Grouping (Record fs), RecVec rs, RecVec rs2', RecVec (rs ++ rs2'))

2023-10-11T17:29:37.542659Z

@ambrosebs would typing clojure.set/rename-keys be a related problem space wrt inference of variables bound by other type variables (f-bounded polymorphism) isn't well supported

2023-10-11T20:03:01.689709Z

@djtango don't think so, is the current annotation not accurate enough?

clojure.set/rename-keys (t/All [x y] [(t/Map x y) (t/Map t/Any x) :-> (t/Map x y)])

Corneliu Hoffman 2023-10-11T20:07:23.652329Z

Ideally we would like a more precise (dependently typed :)) version

2023-10-11T20:09:31.482749Z

yeah, not sure it needs f bounded polymorphism though

2023-10-11T20:17:36.210249Z

I haven't thought about it though, just my first impression.

2023-10-11T20:20:40.406229Z

I was imagining a type more like:

#_#_clojure.set/rename-keys (t/All [[map :< (t/Map t/Any t/Any)]
                                    [kmap :< (t/Map t/Any t/Any)]]
                                   [map kmap :-> (magic ...)])

2023-10-11T20:35:25.733349Z

my magic type ended up slightly ridiculous 🙂

#_(defalias RenameKeys
    (t/TFn [[map :< (t/Map t/Any t/Any)]
            [kmap :< (t/Map t/Any t/Any)]]
           (ReduceKV (t/TFn [map old new]
                            (If (Contains? map old)
                              (Assoc m new (Get map old))
                              m))
                     (Dissoc map (Keys kmap))
                     kmap)))
#_#_clojure.set/rename-keys (t/All [[map :< (t/Map t/Any t/Any)]
                                    [kmap :< (t/Map t/Any t/Any)]]
                                   [map kmap :-> (RenameKeys map kmap)])

2023-10-11T20:36:55.458379Z

might as well just inline the code. does anyone know similar examples in other type systems?

Corneliu Hoffman 2023-10-11T20:37:59.639869Z

What is ReduceKV?

2023-10-11T20:38:55.226709Z

magic nonexistent thing that, if it exsted, would be reduce-kv at the type level, like clojure.core/merge => t/Merge.

🙏 1
2023-10-11T20:40:33.438119Z

it would great to have annotations that work for both the homogeneous and heterogeneous cases. right now, heterogeneous cases are mostly hardcoded in the type system.

2023-10-11T20:41:13.289869Z

the magic type might be the kind of thing that allows users to write their own heterogeneous operations.

2023-10-11T20:42:02.856979Z

for example, t/Merge is hardcoded in the type system, but given t/Assoc, t/Get, it might be possible to be a user extension.

Corneliu Hoffman 2023-10-11T20:42:34.356859Z

I was thinking to write macros

Corneliu Hoffman 2023-10-11T20:42:56.794739Z

I meant to ask, how does t/Get work?

Corneliu Hoffman 2023-10-11T20:43:10.247559Z

the type of get does not involve t/Get

2023-10-11T20:59:19.386059Z

that's just a historical thing, I hardcoded get in the type system and then created t/Get later. at some point I will add the expected annotation.

2023-10-11T21:06:49.231959Z

just like assoc, which actually uses t/Assoc.

2023-10-11T21:07:55.285159Z

my current trajectory is to turn these opaque types into type parameters on IPersistentCollection etc.

2023-10-11T21:09:22.713599Z

e.g., clojure.core/get (t/All [m :< (ILookup ... Get ...), k] [m k -> (Get m k)])

Corneliu Hoffman 2023-10-11T21:10:11.959519Z

Looking forward to this.

Corneliu Hoffman 2023-10-11T21:12:53.981409Z

unrelated question, in my nvim CheckNs works fine but TypeAt always returns v:null

2023-10-11T21:20:46.566179Z

that's good news about CheckNs, go me for 8 year backwards compat. honestly forgot about the plugin!

2023-10-11T21:21:08.076089Z

I think I broke the TypeAt functionality.

Corneliu Hoffman 2023-10-11T21:25:30.244349Z

:) what is your worflow like then?

2023-10-11T21:28:07.388739Z

I have a (is (check-ns ...) in the tests for the project. But it's not a great idea, since check-ns reloads files. it can mess with other tests.

2023-10-11T21:29:34.766599Z

I don't have a great answer.

Corneliu Hoffman 2023-10-11T21:30:58.781249Z

I have the same

2023-10-11T21:31:46.638029Z

I attempted to create an entry point here, it's not quite what I wanted though https://github.com/typedclojure/typedclojure/blob/c0e0b8ac9ab5584204748b947f0b1c80bcb4eabd/typed/clj.checker/src/typed/clojure/main.clj#L136

2023-10-11T21:33:09.927559Z

or maybe I just moved on to the endless other work there is to do. it would be interesting to know if it's useful.

2023-10-11T21:37:02.514589Z

I spend all my time inside the type checker, which is only partially able to check itself. so I don't dogfood this aspect as much as I'd like.

2023-10-11T21:37:43.100369Z

you might find my experiment in type checking malli interesting https://github.com/frenchy64/malli/pull/1/files

2023-10-11T21:38:01.094789Z

that's about the closest I've come using the checker in anger recently

2023-10-11T21:42:49.455209Z

looks like that's a good example of using the exec fn.

2023-10-11T21:43:20.711169Z

and also how to split type checking across separate github actions jobs https://github.com/frenchy64/malli/pull/1/files#diff-8068abef2a43df385283c6bf7dd1b1c51f0a45aa8abb801d4d96fc1bef134795R62-R64

2023-10-11T21:44:40.500929Z

I got further than I remembered. But that doesn't help when you're sitting on a REPL on your own machine. For iterative development I just call (t/check-ns-clj), so basically what you do.

Corneliu Hoffman 2023-10-11T21:46:57.332769Z

Thank you

2023-10-10T15:46:09.184539Z

1. yes. grep for t/TFn here for examples of type functions. t/Transducer is probably the most interesting https://github.com/typedclojure/typedclojure/blob/f22f2b75542d5fa3f8f3e8325fb9494f7799fcf1/typed/lib.clojure/src/typed/ann/clojure.cljc#L245-L249 2. inference of variables bound by other type variables (f-bounded polymorphism) isn't well supported. the other problem is the kind system is very ad-hoc, I happen to be working on this atm. I (think I) recently realized what the kind of dotted type variables (`:..`) which was a bit roadblock https://github.com/typedclojure/typedclojure/blob/f22f2b75542d5fa3f8f3e8325fb9494f7799fcf1/typed/lib.clojure/src/typed/ann/clojure.cljc#L981

2023-10-10T15:52:43.134429Z

I misread your Haskell example, I thought the <= was bounded polymorphism.

2023-10-10T15:57:24.480659Z

(t/defalias MyList
  (t/TFn [[a :< UpperBound, :variance ThisIsInferredSoCanOmit]]
         (t/List a)))

💯 1
2023-10-10T16:00:20.208029Z

could you help me understand the haskell type? guessing these are GADT's?

Corneliu Hoffman 2023-10-10T16:02:00.545349Z

this is the type of leftjoin in the Frames library.

Corneliu Hoffman 2023-10-10T16:02:13.810259Z

in a few meeting will try to write suff up

2023-10-10T16:09:31.841229Z

my main priority is type checking idiomatic clojure code (still some ways to go), but I like to make sure I'm not shooting myself in the foot for such future extensions.

Corneliu Hoffman 2023-10-10T16:11:02.047049Z

What I am trying to do is essentially to typecheck a piece of techml.v3.dataset

2023-10-10T16:11:55.763669Z

feel free to send or describe it

Corneliu Hoffman 2023-10-10T16:13:23.879529Z

thanks, will do/ask when I run into issues. I think your typelevel constructs might just be enough.

🎉 1
2023-10-10T16:14:38.521199Z

have fun!

Abhinav 2023-10-10T03:25:38.602309Z

Hi, I have some dumb questions. I don't know much about implementing type systems for a lang. I noticed that typed.clojure has its own runtime, and its own analyzer (I'm not exactly sure what an analyzer does). Is it absolutely necessary to create a runtime for having a type checker? AFAIK, mypy (a type checker for python) doesn't have its own runtime (I may be wrong). and is the modified analyzer where the type checking takes place?

2023-10-10T15:26:41.884969Z

The runtime is also split so then you don't need to pull in the entire checker in "production".

2023-10-10T15:26:49.800379Z

which has a ton of dependencies

2023-10-10T15:28:04.107259Z

the typed.clojure namespace also lives in that jar. It gives macros to annotate things, such as t/ann for vars and t/ann-form for forms.

2023-10-10T15:29:11.804619Z

thankfully since core.typed became typedclojure, most of the wrapper macros in clojure.core.typed are not needed. so it's primarily for top-level annotations.

2023-10-10T15:30:37.268019Z

there's even a way to avoid using the "runtime" dependency now https://github.com/typedclojure/typedclojure/tree/main/example-projects/zero-deps

2023-10-10T15:31:04.245049Z

useful for libraries that need to distribute without runtime deps

2023-10-10T15:35:55.291279Z

and an "analyzer" outputs a convenient middleground between clojure code and java bytecode for programmatic manipulation. we have our own analyzer because the de-facto analyzer tools.analyzer is too aggressive in macroexpansion. but this is unrelated to how your clojure code compiles. the type checker only runs when you call t/check-ns-clj, so during normal loading the clojure compiler is always used.

2023-10-10T15:37:51.549939Z

during type checking, compilation looks like: read -> analyze (typed.clojure) -> type check -> back to clojure data -> clojure.core/eval usually it's: read -> analyzer (clojure.lang.Compiler) -> .eval

Abhinav 2023-10-10T16:47:16.980429Z

Interesting, thanks for explaining all of this. I appreciate it

👍 1
2023-10-10T03:52:27.349219Z

Not clear what you mean by a runtime

Abhinav 2023-10-10T03:54:14.205439Z

(defproject a-project "0.0.1-SNAPSHOT"
  :dependencies [[org.clojure.typed/runtime.jvm "1.0.1"]]
  :profiles {:dev {:dependencies [[org.clojure.typed/checker.jvm "1.0.1"]]}})
I meant the [org.clojure.typed/runtime.jvm "1.0.1"] part of the project.clj

2023-10-10T03:54:27.825889Z

If you mean https://github.com/clojure/core.typed/tree/master/typed/runtime.jvm that is pretty much core.typed

2023-10-10T03:55:03.123189Z

It is where the code that does the type checking lives

Abhinav 2023-10-10T03:55:37.341589Z

Oh, I see. so it still isn't a replacement for the clojure runtime, correct?

2023-10-10T03:56:36.304479Z

It is for some things, there are some macros in clojure.core that are used a lot that core.typed cannot really handle, the runtime contains replacements that it can

Abhinav 2023-10-10T04:00:56.612559Z

Oh I see