core-typed

2024-03-06T06:09:16.534619Z

I'll be talking about bringing some ideas from the statically typed world to runtime systems like clojure spec.

πŸ‘ 1
😍 4
2024-03-07T17:48:17.320699Z

Trimmed video is here https://www.youtube.com/watch?v=QE3PZLlefUE

2024-03-07T18:51:20.059059Z

That’s great

2024-03-07T19:25:15.957679Z

There’s a lot of boilerplate to create and use the type variables. I wonder if bind-tv could just return the result of t/tv

2024-03-07T19:47:28.097149Z

This is one step away from doing type inference before running the app, reminiscent of ectype by holly wu (https://dejawu.me)

2024-03-08T00:16:34.083609Z

@nbtheduke tv is not really a spec, it's just an occurrence of a type variable that's always substituted away by the time the spec is used.

2024-03-08T00:16:53.203119Z

Can you give an example of what you're thinking?

2024-03-08T00:19:06.711099Z

The main reason tv exists is because of s/explicate. It ties your hands in what your syntax can be.

πŸ‘ 1
2024-03-08T00:32:23.857219Z

Ah that makes sense.

2024-03-08T01:50:57.462909Z

I was thinking of something like generics in other languages where you specify the type variables as local varies and can use them wherever

2024-03-08T01:51:21.899959Z

clojure.spec’s fspecs are already so verbose lol

2024-03-08T02:36:45.432999Z

I tried that at first. after many failed experiments, I succeeded by following fspec's verbosity. It made some things more obvious actually, in particular how to generate a "binder" of type variables, even ones with bounds that depend on each other.

2024-03-08T02:39:07.081099Z

If you distill down typed.clj.spec and this talk, it's really 99% of the work was getting the syntax and substitution right, and 1% was implementing the idea that you can represent a type variable with a small type in a generator, which is directly from quickcheck. https://hackage.haskell.org/package/QuickCheck-2.14.3/docs/Test-QuickCheck-Poly.html

πŸ‘ 1
2024-03-08T04:47:08.683099Z

I wonder if this approach could be brought to Malli

2024-03-08T04:47:34.581759Z

Is the code public?

2024-03-08T05:58:59.460329Z

Here's how you do it for schema as a built in extension https://github.com/frenchy64/schema/pull/11/files And as an external library https://github.com/frenchy64/schema/pull/13/files

2024-03-08T06:24:28.236959Z

Here's a conceivable starting point for malli. As-is, this compiles all polymorphic types to what they would instrument to. The basic idea from here would be to figure out how to preserve the :all schema until you're doing generative testing on it. https://github.com/frenchy64/malli/pull/2/files

2024-03-08T06:27:10.265499Z

If you get that far, here's the skeleton quickcheck code to generatively test a polymorphic spec (doesn't implement the "trick" of pushing gensyms through the function, see spec impl for that) https://github.com/frenchy64/schema/pull/13/files#diff-8cf7ec1a3263df0e063ba2a470023329ca838dd99ef8b4137c2cd9348f92df6bR31-R46

2024-03-08T06:28:47.101899Z

And presumably that will break the instrumentation side, and to fix it you need to instantiate the spec when you instrument it like here https://github.com/frenchy64/schema/pull/13/files#diff-06186e539315e4b79419d0a1bf50a613c059f45e5246aa4c0416cebc67cea263R267

2024-03-08T06:32:39.459449Z

Oh, looks like I did implement this as a real library for schema. here it is https://github.com/frenchy64/schema-incubator But I think I didn't port over the generative testing part from that PR above. I lost steam at some point.

2024-03-08T14:55:05.614849Z

makes sense, seems like a ton of work

2024-03-08T14:55:10.772439Z

thanks for the links, i'll read through them

2024-03-08T16:30:08.559549Z

I guess it was, thanks. I think I only stopped because I never got much interest in typed.clj.spec and the maintainer of schema wasn't interested in this feature being merged which led to a laborious process of extracting a library. Popped my bubble for a few years apparently, but this work is some of my favorite!

🀝 1
2024-03-08T16:33:08.321409Z

i've been working more with ocaml recently and i would love for clojure to have something similar. this kind of work is about as good as we can currently get. (i have no idea how you'd handle macros in any type system lol, you'd probably have to macro-expand everything and then run the inference on the resulting forms)

2024-03-08T16:33:49.129459Z

Oh I have ideas!

2024-03-08T16:34:27.144309Z

This was going to be 2 talks actually. Leveling up function specs, and then macro specs.

πŸ‘ 1
2024-03-08T16:36:58.887279Z

maybe i'm biased but i really don't like clojure.spec lmao, i want to see this done separate from it. i gotta spend more time with typedclojure, i think

2024-03-08T16:39:13.043919Z

part of the motivation here is to make polymorphic types ubiquitous. then typed clojure can steal them for free instead of having to demand for so many annotations. and then we can translate polymorphic types to a library that implements this runtime checking idea, and get generative testing for free.

2024-03-08T16:39:53.475789Z

also I'm a big fan of just implementing better generators for test.check, but spec kind of killed that area it seems.

2024-03-08T16:40:00.676269Z

we don't even have function generators in test.check.

2024-03-08T16:51:48.189969Z

it's possible I even implemented this for typed clojure and never told anyone https://github.com/typedclojure/typedclojure/blob/7ef50ae3db663a713960a06b804ca0927da71a92/typed/clj.checker/src/typed/clj/generators.clj#L120

πŸ˜‚ 1
2024-03-08T16:52:11.506569Z

you've done so much, i'm surprised you can remember all of this

2024-03-08T16:52:56.234499Z

Haha clearly I've forgotten most of it!

2024-03-08T16:56:37.260469Z

Well, favor #2 would be: please boot up a repl and see if those typedclojure check tests work πŸ™‚

2024-03-06T16:48:50.746969Z

sadly, I will be putting the kids to bed at that time, but I can't wait to watch it tomorrow

Ben Sless 2024-03-14T09:15:48.023009Z

I tried applying a very similar notion to malli using datalog syntax, which is a fun cheat when you're immutable, to treat the input and output as relations. It also lets you constrain the inputs like you wouldn't be able to otherwise, e.g.

[:map {:in ['e] :where '[[?e :a ?a] [?e :b ?b] (< ?a ?b)]} [:a :int] [:b :int]]
Apply to functions at will

Ben Sless 2024-03-14T09:16:21.708969Z

I think this representation lends itself well to programmatic manipulation and value generation but I have no proof yet

Ben Sless 2024-03-14T09:26:57.864689Z

For functions it would look sort of like

;; ID
[:=>
 '{:in [[?in] ?out]
   :where
   [(= ?in ?out)]}
 [:cat :any] :any]


;; comp
[:=>
 '{:in [[f g] h]
   :where
   [[(f ?a) ?b]
    [(g ?b) ?c]
    [(h ?a) ?c]]}
 [:cat
  [:=> [:cat :any] :any]
  [:=> [:cat :any] :any]]
 [:=> [:cat :any] :any]]

Ben Sless 2024-03-14T09:28:07.644679Z

I think this sort of creates the same data flow graph

2024-03-14T11:45:41.943269Z

That’s a nice syntax

Ben Sless 2024-03-14T12:38:32.052929Z

I believe the right syntax for relational problems should also be borrowed from a relational language πŸ™ƒ

2024-03-14T16:03:11.012119Z

That's cool. Have you tried applying this to sequence regular expressions?

2024-03-14T16:04:24.633079Z

Like say the kw args version of your :map spec.

Ben Sless 2024-03-14T16:17:26.663249Z

I invented a sort of ass backwards pattern matching syntax partially inspired by scheme syntax case and meander to represent repetition

2024-03-14T16:18:24.208799Z

How might you psuedocode the spec for clojure.core/map?

Ben Sless 2024-03-14T16:21:59.789529Z

I'm on my phone so going to be extremely hand wavy -

[?in [f [?x ...]]]
[?out [?y ...]]
[(f ?x) ?y]

Ben Sless 2024-03-14T16:24:19.712799Z

I think that's sufficient for drawing all the arrows

2024-03-14T16:24:24.712129Z

Ok I'm starting to see it.

Ben Sless 2024-03-14T16:25:46.845839Z

I think it's more difficult with things like unordered data with non canonical representation which @noprompt directed me to the research in the Egison language for

2024-03-14T16:27:22.585819Z

I like that it's parsable so you can use it for generators, instead of relying on an opaque function to tighten a dependent spec.

2024-03-14T16:28:35.408279Z

Like [:map {:fn (fn [k v] (< k v))} :int :int] would be tough to generate.

Ben Sless 2024-03-14T16:30:25.682979Z

It would be impossible to generate! But its a relation, so it should be trivial to generate

Ben Sless 2024-03-14T16:31:35.043499Z

That's my big contribution - in a functional language the input and optional outputs of a spec form a relation Generating relations is easy The hard part is you'll have to write a relational eval for Clojure πŸ™‚

Ben Sless 2024-03-14T16:35:45.600029Z

If William Byrd is looking for disciples students I'm game

πŸ˜„ 2
2024-03-14T16:47:56.106089Z

It's interesting I'm coming from the opposite side. I'm fascinated by how polymorphic types imply relations.

Ben Sless 2024-03-14T16:58:09.394829Z

Every sufficiently advanced type system contains a poorly specified bug ridden implementation of Prolog

πŸ˜‚ 2
Ben Sless 2024-03-14T16:59:03.174939Z

So just lean into it and use relational programming as the bedrock

2024-03-14T17:10:08.376599Z

Pretty sure I implemented a Typed Racket in minikanren. It had some drawbacks πŸ™‚

Ben Sless 2024-03-14T17:16:16.107809Z

Such as?

2024-03-14T17:20:05.195459Z

Probably stemming from all the concepts not having bedrock in relational programming πŸ™‚ encoding subtyping and occurrence typing was difficult and slow I think.

2024-03-14T17:27:27.193799Z

Did you look into generating values from relations?

Ben Sless 2024-03-14T17:29:43.214259Z

Not yet, I actually assumed that would be trivial as each additional clause just adds a bind

Ben Sless 2024-03-14T17:31:16.239109Z

I have about three reimplementations of core logic in various degrees of incompleteness

Ben Sless 2024-03-14T17:31:51.880449Z

I want a high performing implementation which would support unordered collections from the get go, not just sequences

2024-03-14T17:35:47.044039Z

Do you think generating functions might be trivial?

Ben Sless 2024-03-14T17:42:59.189989Z

With a relational evaluator, sure

Ben Sless 2024-03-14T17:45:13.019699Z

Not sure how you'll do narrowing with this

2024-03-14T17:54:29.177789Z

Keep me posted on your progress.

Ben Sless 2024-03-14T17:58:30.849099Z

expect very little unless I get a sabbatical or lots of free time otherwise. Kids are loads of fun, but also a time sink πŸ™ƒ As you can see it's still sitting pretty high at the top of my mind so it'll probably be the first thing I get to when I do have time

πŸ’― 1
❀️ 1
2024-03-14T18:04:51.600259Z

I mean I'm 12 years into Typed Clojure, no rush. πŸ˜‰

Ben Sless 2024-03-14T18:07:10.891489Z

MiniKanren is sort of why I ended up here πŸ™‚