Fork me on GitHub
#core-typed
<
2020-06-04
>
ambrosebs01:06:45

for anyone interested in reusing some of Typed Clojure's infrastructure for their own type system, https://www.patreon.com/posts/example-type-37870230 shows how to use its analyzer.

didibus08:06:39

@U04V15CAJ No idea if this could be useful to the type checking in clj-kondo or not, but in case you don't monitor this channel, and it might be useful.

ambrosebs01:06:56

@U04V15CAJ if you ever find yourself wanting to partially expand something, it'll probably be useful. I think you use rewrite-clj right? my analyzer is intended for the use-case where you use tools.analyzer, but you need to check intermediate steps.

borkdude10:06:53

tools.analyzer is not suited for clj-kondo, it doesn't work with GraalVM unfortunately

ambrosebs14:06:42

oh interesting. my analyzer is a distinct entity from tools.analyzer (w/ no transitive dependency on it), do you know what the issues are? I might be able to fix them.

borkdude14:06:16

I think one issue was that tools.analyzer did macroexpansion which effectively uses eval and eval uses dynamic classloading which is not supported by GraalVM

ambrosebs14:06:25

well, that's bad news 😞

borkdude14:06:40

It's basically why I wrote a clojure interpreter 😉

borkdude14:06:42

I'm working on a new macroexpand feature in clj-kondo that will be using this interpreter, so it can all still run in the native binary: https://clojureverse.org/t/feedback-wanted-on-new-clj-kondo-macroexpansion-feature/6043 feedback on that is most welcome

ambrosebs14:06:12

nice. I've thought a lot about this kind of thing. in typed clojure, the analyzer can return an ::unanalyzed AST node, and then custom rules can be registered about how to type check a particular macro.

ambrosebs14:06:37

I haven't figured out how to properly handle macros that bind variables, but I would go in the direction of extending a type environment and passing that down, rather than your sketch of expanding to a let (FWIW).

borkdude14:06:57

the clj-kondo approach is not only about validating a macro. it's foremost about expanding since it can't do that without eval. but since the user provides the code that macroexpands, it can do all kinds of checking on it as well and throw an exception. I'll probably let the user return an :findings vector too.

borkdude14:06:45

potentially the expand function could get access to locals, etc too. that's why the function signature is a map to a map, to allow for more stuff in the future

ambrosebs14:06:31

yea cool that sounds right to me

ambrosebs14:06:12

I like this direction. does your interpreter expose a macroexpand-1 function?

borkdude14:06:39

it does:

$ bb "(defmacro foo [x] (list 'do x x)) (macroexpand-1 '(foo 1))"
(do 1 1)

borkdude14:06:51

(bb = babashka which uses the same interpreter)

ambrosebs14:06:53

ah interesting. can you rebind tools.analyzer's macroexpand-1 with that?

borkdude14:06:08

you mean, like, alter-var-root?

ambrosebs15:06:05

it has its own macroexpand-1

ambrosebs15:06:40

that's the direction I was thinking, I'm sure there's a ton of other details

ambrosebs15:06:54

(for graalvm + tools.analyzer compat)

borkdude15:06:10

I think this boils down to the reason why clj-kondo doesn't just run the code from a user's source file: because it has to be pure clojure, no references to other namespaces or particular Java classes, protocols, etc

ambrosebs15:06:57

appreciate your time and insight!