core-typed

whilo 2023-09-20T16:39:52.502339Z

@ambrosebs i am very happy to see that you made so much progress, i need to check out the latest typedclojure version

2023-09-20T18:23:39.822289Z

Thanks.

whilo 2023-09-20T16:41:06.708939Z

what is the status of clj-kondo integration? it would be really really nice to have typedclojure always there and integrate with this more pragmatic tooling pipelines

whilo 2023-09-20T16:45:26.862729Z

it would also motivate people to write type signatures from the get go

2023-09-20T18:28:42.249859Z

In terms of running typedclojure without a REPL, that's not going to happen yet. Too much runtime information is used currently. I haven't looked into converting clj-kondo annotations to typedclojure yet. In terms of piggybacking off clj-kondo's caching and automatically type checking when clj-kondo senses a change, I briefly looked into it but decided to build a more robust classpath watching system native to typedclojure. The prototype is here https://github.com/typedclojure/typedclojure/blob/main/typed/clj.checker/src/typed/clojure/main.clj#L136

2023-09-20T18:30:44.670579Z

there's also typed.clojure/check-dir-clj for checking directories of code. So there's a bunch of small pieces there, but no coherent story yet.

2023-09-20T18:33:28.231159Z

I think caching type checking results will be very important given the liberties I'm taking trading performance to type check more expressions (symbolic execution). So I think this part will need to be carefully designed, and then retrofitted to clj-kondo integration.

2023-09-20T18:33:43.682989Z

but given the fact that we need a REPL, it might be a non-starter. we'll see.

whilo 2023-09-20T16:43:52.993989Z

i have a few opinions regarding clojure as an environment to bootstrap towers of languages. it would be good to have a bit more racket tooling in general, @ambrosebs what is your take on https://docs.racket-lang.org/turnstile/ and integrating a framework to build abstract interpreters?

Ben Sless 2023-09-25T13:48:31.920419Z

Do you have to have a relational interpreter for partial evaluation? I thought a staging interpreter was sufficient

Ben Sless 2023-09-25T13:49:03.389889Z

Like in Nada Amin's papers on collapsing reflective towers

Ben Sless 2023-09-25T13:50:40.433489Z

Relational interpreters can be used to check typing though (Greenspun's twelfth rule)

Ben Sless 2023-09-25T13:54:00.035679Z

This is pretty much my white whale whilo I'd very much like to explore this field

👏 1
Ben Sless 2023-09-26T04:43:16.875429Z

whilo 2023-09-27T18:07:33.977769Z

Hey, sorry, just saw this. I agree @ben.sless, it should be a tower of relational interpreters. A first step I would like to see better PL tooling, e.g. for building interpreters. Do you have a take on this?

Ben Sless 2023-09-27T18:37:46.903099Z

I'm still not sure why they need to be relational, but off the top of my head • start with a minimal interpreter for Clojure with the requirement it be metacircular • add staging • ensure a tower of these interpreters can be collapsed • convert the interpreter to a relational interpreter That exercise lets you identify what tools are missing, then build them and port the interpreter to them. Maybe I'm off base, but the requirement for better PL tooling is too broad for me to imagine what exactly you're looking for and how you'd want to use it

Ben Sless 2023-10-03T07:06:09.542539Z

Mine or Ambrose's?

Ben Sless 2023-10-03T07:07:08.352209Z

I'll dig it up when I get home but I focused my macrology on specifying the expression children at definition time to emit the walking and update code

Ben Sless 2023-10-03T18:33:30.572319Z

Here

(defmacro defop
  "Adds op, env, form fields, first group of fields is children, rest are
  not children but still AST fields."
  [name children extra-fields & opts]
  `(defrecord ~name [~'op ~'env ~'form ~@children ~@extra-fields]
     p/IChildren
     (~'-children* [~'_]
       ~(mapv (fn [c] [(keyword c) c]) children))
     (~'-children [~'_]

      ~(if (some #(:vector (meta %)) children)
         (-emit-children-cat children)
         (mapv symbol children)))
     p/IUpdateChildren
     (~'-update-children [~'ast ~'f]
      ~(-emit-update-children 'ast children 'f))
     ~@opts))

Ben Sless 2023-10-03T18:36:36.880659Z

Then this expands like:

(defop If [test then else] [children])
(defrecord
  If
  [op env form test then else children]
  p/IChildren
  (-children* [_] [[:test test] [:then then] [:else else]])
  (-children [_] [test then else])
  p/IUpdateChildren
  (-update-children
    [ast f]
    (let [G__11305 ast
          G__11305 (update-child G__11305 f :test test)
          G__11305 (if (reduced? G__11305)
                     G__11305
                     (update-child G__11305 f :then then))
          G__11305 (if (reduced? G__11305)
                     G__11305
                     (update-child G__11305 f :else else))]
      G__11305)))

Ben Sless 2023-10-03T19:56:25.764709Z

Next step is figuring out how I can avoid intermediate allocations during update-children

Ben Sless 2023-10-02T07:42:27.030279Z

What about https://github.com/pangloss/pattern?

whilo 2023-10-02T08:10:54.629889Z

That looks super interesting!

2023-10-02T20:40:15.394469Z

@ben.sless regarding tools.analyzer with records, I did basically that with my fork. These macros might come in handy https://github.com/typedclojure/typedclojure/blob/a74c51a8242bdb747e1e79e969c98fe31a4d3e36/typed/cljc.analyzer/src/typed/cljc/analyzer.cljc#L144-L220 Grep around the codebase for those 3 operators and you'll probably get the idea, basically defrecord => defexpr, ->Record => (create-expr {..} Record), and assoc => (update-expr expr Record [k v] [k v]). e.g., browse this file from here, you'll probably recognize most of it from tools.analyzer https://github.com/typedclojure/typedclojure/blob/a74c51a8242bdb747e1e79e969c98fe31a4d3e36/typed/cljc.analyzer/src/typed/cljc/analyzer.cljc#L311

Ben Sless 2023-10-03T04:22:48.875999Z

I tried to get clever in my implementation and get rid of the op keyword and implemented walk directly instead of via children It was a slog so I put it aside

Ben Sless 2023-10-03T04:23:07.200769Z

I'll get back to it, sometime in the future

whilo 2023-10-03T05:53:53.317969Z

Interesting, your dispatch was slower than accessing the keyword?

2023-09-20T18:36:06.267639Z

turnstile is great. You can build custom typing rules for macros in typedclojure but they only fire during type checking. So unlike turnstile, you don't have access to the type checker during normal expansion/compilation.

2023-09-20T18:39:30.779809Z

typedclojure is just a linter in that respect. there are various experiments in combining the two (there's an unused extensible #lang system in typedcloure), but in the end they interfere with IMO one of the strongest features of typedclojure which is you can just turn it off by simply compiling the file as usual. checking is completely optional. so I've been leaning into complete separation of runtime expansion and type checking expansion, and haven't fully explored a turnstile-like approach, but the pieces are there.

whilo 2023-09-20T18:41:15.640219Z

how hard is it to go into the direction of dependent types?

whilo 2023-09-20T18:41:33.041809Z

i would like to have a general relational abstract interpretation framework for clojure

whilo 2023-09-20T18:41:53.080709Z

to allow things like partial evaluation, which works remarkably well if you know that expressions are pure

2023-09-20T18:42:05.098869Z

could you drop an example and I'll think about it

whilo 2023-09-20T18:43:09.890909Z

have you seen will byrds work on barliman and relational scheme?

2023-09-20T18:43:24.489899Z

I saw the talk IIRC

2023-09-20T18:43:30.369759Z

at a conj

whilo 2023-09-20T18:43:34.286189Z

right

whilo 2023-09-20T18:44:26.221199Z

i think a stack of languages like this (e.g. like a fused nanopass stack) for a compiler framework would be very powerful to build runtimes and inference engines

whilo 2023-09-20T18:45:27.611269Z

mostly i am wondering whether you are interested in a set of general purpose libs for interpreter building including type checkers

whilo 2023-09-20T18:45:41.189749Z

which do bidirectional abstract interpretation usually

2023-09-20T18:48:35.070209Z

I kind of understand those words but I'm lacking a precise understanding of them

whilo 2023-09-20T18:49:19.841299Z

on a basic level i would appreciate tooling to build towers of interpreters

whilo 2023-09-20T18:49:27.931549Z

i think racket and scheme have quite a bit of work there

whilo 2023-09-20T18:49:45.279409Z

and i think you know more about the technical details than i do probably

whilo 2023-09-20T18:50:20.344689Z

i have built this multipass compiler https://github.com/plai-group/daphne with aggressive partial evaluation

whilo 2023-09-20T18:50:38.129249Z

and compile neural network architectures for probabilistic inference basically

whilo 2023-09-20T18:50:43.907999Z

(writing my thesis about it atm.)

whilo 2023-09-20T18:50:53.739879Z

but i have not done a lot pl work beyond that yet

2023-09-20T18:51:40.474899Z

are nanopass and towers of interpreters related?

whilo 2023-09-20T18:52:17.309499Z

they implement towers of languages

whilo 2023-09-20T18:52:37.530719Z

nanopass fuses the compiler so that you don't have to run each pass explicitly

whilo 2023-09-20T18:52:51.540539Z

but are as efficient as a one/few pass compiler

2023-09-20T18:53:41.833249Z

ah so you use nanopass to efficiently collapse the tower?

whilo 2023-09-20T18:53:59.559789Z

my tower is not that high

whilo 2023-09-20T18:54:12.759969Z

haven't been able to put in effort into better PL abstractions

2023-09-20T18:54:24.259559Z

I just meant in general, trying to unrust my PL knowledge 🙂

whilo 2023-09-20T18:54:27.162319Z

that is why i am reaching out to you

whilo 2023-09-20T18:55:10.752479Z

right

whilo 2023-09-20T18:55:32.066149Z

what would you need to have better symbolic interpretation?

whilo 2023-09-20T18:55:44.115169Z

e.g. to facilitate stronger forms of logic

2023-09-20T18:56:44.123899Z

I basically just implement beta reduction. what are other strategies?

whilo 2023-09-20T18:58:13.928399Z

i see. i thought that ghc and some other compilers can also infer backwards from return values to inputs (and any other direction)

whilo 2023-09-20T18:58:25.129919Z

i guess most type checkers propagate forward though

2023-09-20T18:58:42.647559Z

ah yes. typedclojure is just forwards.

2023-09-20T18:59:02.268609Z

mostly because local type inference is eager in that way

2023-09-20T18:59:30.914679Z

my symbolic execution amounts to little tricks delaying type checks until local type inference can kick back in again

whilo 2023-09-20T19:00:23.033199Z

i see

whilo 2023-09-20T19:00:26.217879Z

makes sense

whilo 2023-09-20T19:01:15.561069Z

have you thought about dependent types? f* or lean can discharge to z3

2023-09-20T19:04:49.105239Z

it's not my area no.

whilo 2023-09-20T19:05:24.610819Z

meaning that they have abstractions over representing the type constraints

whilo 2023-09-20T19:05:31.577009Z

ok

whilo 2023-09-20T19:05:47.239109Z

spec is very dependent type like

2023-09-20T19:05:53.809739Z

I think occurrence typing might be roughly related.

2023-09-20T19:06:43.593129Z

for example you can learn the length of a vector and assert that you only nth a valid length.

2023-09-20T19:07:03.047509Z

that has a flavor of dependent types but the formalism is probably very different

2023-09-20T19:07:25.205889Z

plus all the hacks/extensions I've made might make the research irreconcilable with my impl

2023-09-20T19:09:14.933869Z

there's a lot of overlap between spec and typedclojure's types, so it might fit your conception. I don't have a strong sense of when you're allowed to use the Dependent term to characterize a type system.

2023-09-20T19:10:30.637879Z

though I'm sure it's very specific and not related to my work

2023-09-20T19:11:14.627069Z

what part of spec says dependent to you?

whilo 2023-09-20T19:11:47.549009Z

i can define types with predicates that are turing complete

2023-09-20T19:12:17.571419Z

ok yep that makes sense.

2023-09-20T19:15:03.233959Z

I mean a symbolic closure type is just code that we beta expand with types. surely I'm flirting with that idea at least.

2023-09-20T19:16:08.614879Z

I never understood how checking and evaluation actually is interleaved in dependent typing, does the checker literally run during runtime?

2023-09-20T19:16:56.328639Z

I know turnstile can check during a macro expansion, but does it check during runtime?

2023-09-20T19:17:41.611229Z

or do turnstile rules just expand to code that checks at runtime?

2023-09-20T19:18:48.239899Z

perhaps I'm thinking of that "other" more recent dependent type system in racket. I think it starts with a C.

2023-09-20T19:20:16.143259Z

erm maybe I'm just thinking of https://www.williamjbowman.com/resources/wjb2019-depmacros.pdf

2023-09-20T19:21:27.585249Z

Cur

2023-09-20T19:22:27.188879Z

we might need to circle around the topics you want to talk about until I catch up. I think you're right that I probably can help.

whilo 2023-09-20T19:24:41.653629Z

oh yeah, william is here in the department

whilo 2023-09-20T19:25:33.108049Z

yes, i think you need to add runtime checks

whilo 2023-09-20T19:25:47.188859Z

i would rather think of runtime as a continuation of compile time

whilo 2023-09-20T19:26:00.329959Z

i think the whole distinction of statics and dynamics is a bit overdone

👍 1
2023-09-20T19:26:16.968679Z

oh I thought you were in Europe. nice

whilo 2023-09-20T19:26:36.258929Z

i am german, but live in vancouver atm.

2023-09-20T19:27:03.431399Z

yeah I hope no-one ever asks me to choose static vs dynamic in an argument.

2023-09-20T19:27:23.379509Z

it's one big sludge in my head at this point 😉

whilo 2023-09-20T19:27:31.338189Z

hehe

2023-09-20T19:28:25.218079Z

that makes sense. similar to gradual typing.

whilo 2023-09-20T19:28:37.377229Z

thinking about the bar based tree syntax of rules for proofs, you can push type constraints back and forward whenever you get them along the tree and resolve it

whilo 2023-09-20T19:28:51.569979Z

that is why i think it should be a relational interpreter

2023-09-20T19:29:55.929839Z

"it" is what here?

2023-09-20T19:30:45.110529Z

daphne?

whilo 2023-09-20T20:26:24.310919Z

hehe

whilo 2023-09-20T20:26:41.807549Z

well, yes. i mean in general interpreter frameworks

whilo 2023-09-20T20:34:52.156169Z

do most type systems you know just follow beta reduction?

2023-09-20T22:30:27.024929Z

no, comparatively what I'm doing is insane. type systems don't usually put code in types. kind of defeats the point of a type.

2023-09-20T22:31:35.152299Z

the trick with local type inference is transferring expected types to/from functions/arguments at application points.

2023-09-20T22:32:31.548449Z

you usually need to make a choice: check arguments first, or check function first.

2023-09-20T22:33:00.518089Z

if you check function, then you have expected types for args. useful if you have hard-to-infer args like functions.

2023-09-20T22:33:35.524819Z

if you check args, then you have expected types for fn params, which is the classic problem in local type inference: where to find types for params.

2023-09-20T22:34:06.957539Z

there's all sorts of papers with specific rules about when to do which strategy, or hybrids.

2023-09-20T22:35:19.992939Z

I just threw all that out the window. if you find a hard-to-infer expression (i.e., lambdas), then just stuff its code into its type, then wait for it to be applied via the application rule or subtyping etc

2023-09-20T22:36:41.754099Z

I forgot the specific strategy, but standard ML does something similar in spirit. IIRC it effectively inlines lambdas to its application position.

2023-09-20T22:37:41.182789Z

you don't need major changes to the formalisms for all these other strategies. I'm not aware of anything trying to formalize my strategy, except maybe symbolic execution land. I had a draft in my phd.

2023-09-20T22:40:24.920259Z

it's probably most similar to how Typed Racket checks function intersections. you check the function body multiple times for each type the function is supposed to have. the intuition with my approach is that you build up that intersection as you type check.

whilo 2023-10-02T04:06:15.578229Z

@ben.sless Sorry for just throwing this at you without more context, I was in a meeting. What I mean is that racket has a lot of libraries that help to implement interpreters, in particular redex. I have seen very little work in Clojure so far trying to make it easy to port racket language implementations or features over to DSLs that also work in Clojure. Nada's racket implementation of the tower for instance would be readily portable with a few primitives from redex https://github.com/TiarkRompf/collapsing-towers/blob/master/popl18/core.rkt#L2.

whilo 2023-10-02T04:09:50.487889Z

Clojure compilers typically are one-pass, directly hooking into tools.analyzer. But even the AST of the analyzer is not optimal if you compare it to chez scheme for example (or also the datalog parser we use in datahike). There you use dedicated types (e.g. defrecords) to make dispatch on nodes super fast without having to access datastructures to dispatch. nanopass can then collapse the tower of compilers and erase the overhead of having many passes and languages. I am trying to figure out atm. how to do this with Clojure.

whilo 2023-10-02T04:11:54.554359Z

I think the limited engagement with racket is a huge lost opportunity for Clojure, because people in racket do a lot of work implementing languages, but racket will never be a mainstream industrial environment, I think, because too few people in its ecosystem care about that. But making it easy for them to ship a language of theirs to the JVM or JavaScript would for sure be interesting enough so that they would also promote Clojure to PL researchers.

whilo 2023-10-02T04:12:22.049789Z

Would be curious what @ambrosebs’s take is on this.

whilo 2023-10-02T04:15:01.387439Z

It is unlikely that we would get any stack of building something like this optimal in the first go, figuring out how to build supporting tooling to do so would amortize the cost of iteration.

whilo 2023-10-02T04:15:30.796229Z

Having said all this, I think most PL rewriting systems are fundamentally unidirectional, i.e. they reduce terms forward and are not really relational.

Ben Sless 2023-10-02T04:16:23.794369Z

@noprompt is working on something interesting, maybe he wants to chime in

👍 1
Ben Sless 2023-10-02T04:17:09.064139Z

I'll take some time to digest this, but regarding performance etc, I've started sketching out a port of tools analyzer to records

❤️ 1
whilo 2023-10-02T04:17:28.855269Z

I would like to go the other way and start bottom up with Datalog, similar to https://dl.acm.org/doi/10.1145/3591239 and use the persistent indices we have in datahike now.

whilo 2023-10-02T04:17:49.567479Z

Nice!

whilo 2023-10-02T04:18:36.470009Z

I think porting some of the typical special forms of redex might pay off a lot to integrate into racket. It would be good to have somebody with a bit more racket experience than me though.

whilo 2023-10-02T04:22:07.166459Z

@ben.sless I have built https://github.com/plai-group/daphne which has multiple passes but operates naively on sexps, which is convenient, but not ideal.

whilo 2023-10-02T04:22:48.468249Z

First order languages like this can be integrated with Datalog and restricted relational languages that could have termination guarantees and cost models.

whilo 2023-10-02T04:23:34.407499Z

One thing I would like to be able to do is fit a measure estimator (e.g. tranforrmer-based diffusion model) automatically to your database queries to invert certain relations effectively.

whilo 2023-10-02T04:26:45.862059Z

E.g. summarized here https://icml.cc/virtual/2023/oral/25446

whilo 2023-10-02T04:34:34.795969Z

In other words a strict generalization of relational programming is measurable programming (or probabilistic programming), i think this would fit in nicely into a relational interpreter stack that can do many different forms of inference.

whilo 2023-09-20T18:40:50.069489Z

(as a side note: it is driving me a bit crazy that the clojure macroexpander is throwing away metadata, in particular line numbers)