core-typed

yuhan 2025-03-18T18:00:25.764239Z

Is there any way to get at the 'data representation' of a type error? It feels strange to interact with core.typed primarily by reading the std output of the typechecker, instead of being able to somehow inspect it as data at the REPL.

yuhan 2025-03-20T10:00:40.505839Z

Any easy way of trying this out? I tried referencing it as a gitlib in my deps.edn but that didn't work (figured it out - I cloned the repo and used :local/root)

2025-03-20T14:39:26.351539Z

For the gitlib, you need to depend on the checker itself. The root deps.edn is just for local dev https://github.com/typedclojure/typedclojure/blob/main/typed/clj.checker/README.md#releases-and-dependency-information

2025-03-18T18:22:29.447299Z

yes, I haven't ported them to typed.clojure yet but try clojure.core.typed/check-{ns,form}-info

2025-03-18T18:23:24.894159Z

the docstrings should describe how to use the output

yuhan 2025-03-18T19:05:11.887789Z

hmm, I can't seem to get anything like what's printed to stderr from the return value of those functions. For example:

(clojure.core.typed/check-form-info '(+ 1 "oops"))
This prints:
Type Error (/Users/yuhan/scratch/typed_example/minimal.clj:13:40) 
Function + could not be applied to arguments:


Domains:
	Long :*
	(t/U Double Long) :*
	t/AnyInteger :*
	t/Num :*

Arguments:
	(t/Val 1) (t/Val "oops")

Ranges:
	Long
	Double
	t/AnyInteger
	t/Num




in:
(+ 1 "oops")
I want to obtain a data structure containing the above information, is this possible?

yuhan 2025-03-18T19:09:21.796149Z

Poking around in the returned map only gets me the location of the errors:

(-> (clojure.core.typed/check-form-info '(+ 1 "oops"))
      :ex
      (ex-data)
      :errors
      (nth 0)
      (ex-data))
  ;; => {:type-error :clojure.core.typed.errors/type-error,
  ;;     :env
  ;;     {:line 15,
  ;;      :column 44,
  ;;      :file "/Users/yuhan/scratch/typed_example/minimal.clj"},
  ;;     :form (+ 1 "oops")}
whereas I'm after something (isomorphic to)
{:cause "Function + could not be applied to arguments"
 :form (+ 1 "oops")
 :function clojure.core/+
 :domains #{[Long :*] 
            [(t/U Double Long) :*] 
            [t/AnyInteger :*] 
            [t/Num :*]} 
 :arguments [(t/Val 1) 
             (t/Val "oops")] 
 :ranges #{Long 
           Double 
           t/AnyInteger 
           t/Num}}

2025-03-18T19:26:34.147729Z

Ah, yes that's not available. That information is thrown away after the error msg is generated. I'll have a look at where that can be attached.

2025-03-18T19:27:21.859549Z

I'll have to go through all the different kinds of errors and make a data format for each.

yuhan 2025-03-18T19:41:33.536189Z

Thanks! It would be a huge deal if users could access and manipulate that data - e.g. at the very least being able to customize the verbosity of the output format

yuhan 2025-03-18T20:03:00.674359Z

For some more context, I'm looking to provide type-based validation and user feedback for a custom DSL, and seeing if it's possible to build it on top of some other static analysis framework like Typed Clojure or clj-kondo.

yuhan 2025-03-18T20:11:27.714639Z

You might probably be familiar with Malli's approach towards error messages which very neatly separates the concerns into explain and humanize phases – are there technical reasons why Typed Clojure could not operate in that manner? (apart from the large amount of refactoring it would undoubtedly take)

2025-03-18T20:11:40.989199Z

I was a naive undergrad when I wrote it

2025-03-18T20:12:01.848879Z

spec was a huge revelation, but postdated this code

2025-03-18T20:12:39.913789Z

should be trivial to surface the information, I can do some cases now

✨ 2
yuhan 2025-03-18T20:23:39.520949Z

Yup, I imagine also that this "dump info to stderr" approach is the norm for type checking facilities in most other languages, my expectation of having first-class access to the analysis data is probably a cultural thing :P

2025-03-18T20:24:18.645039Z

yes it was transliterated from Typed Racket.

😮 1
2025-03-18T20:24:21.834879Z

spot on

2025-03-18T20:25:44.640669Z

it would be pretty cool if I could return enough information to restart type checking via a continuation with different types 🙂

2025-03-18T20:26:10.093159Z

but I have no idea how to do that

2025-03-18T20:26:39.207919Z

a little box pops up that lets you try plugging different types

yuhan 2025-03-18T20:33:34.436669Z

Yeah - it would be nice being able to leave a placeholder and having the type checker suggest possible values to fill it in with, something you see all the time in strongly typed languages

yuhan 2025-03-18T20:35:13.460759Z

although Clojure's probably too dynamic in general to be able to narrow things down to something useful, but it's one of my goals for the DSL which operates in a much more constrained domain

👍 1
2025-03-18T20:56:23.598039Z

Made some progress, I'll give it a bit more thought and let you know how I go.

yuhan 2025-03-18T20:58:56.513739Z

Amazing, thanks! I definitely wasn't expecting such quick turnaround

yuhan 2025-03-18T21:05:18.139319Z

One more tiny bit of feedback - the entry point functions in typed.clojure could probably do with expanded docstrings explaining their arguments or linking to another var which does, eg. for t/check-ns-clj

See `typed.clj.checker/check-ns` for available keyword arguments

2025-03-19T06:02:36.412449Z

Made a data representation for the two most common errors https://github.com/typedclojure/typedclojure/blob/main/doc/errors.md

🎉 1