core-typed

Typed Clojure, an optional type system for Clojure
zilti 2020-02-22T00:00:51.227500Z

No, sounds pretty familiar. I also really discovered a cider feature recently that lets you mark functions in your code, and when you do something in the REPL that calls that function, CIDER will live-display the variable values inside your code

zilti 2020-02-22T00:01:17.227900Z

But maybe I gotta try it. Maybe it throws stuff at me that makes me reconsider

2020-02-22T00:03:42.228400Z

also @ambrosebs maybe an inspiration for your web type checking https://borkdude.github.io/sci.web/

2020-02-22T00:06:19.229Z

Do you know about clj-kondo btw? It's by the same guy

2020-02-22T00:07:09.229800Z

He does simple type checking (effective, but probably not allowed to call it type checking)

2020-02-22T00:07:51.230Z

#clj-kondo

2020-02-22T00:11:32.230800Z

yea @borkdude reached out to me a few days ago about clj-kondo

2020-02-22T00:12:13.231200Z

it's an opt-in linter, which is basically what typed clojure is 🙂

2020-02-22T00:12:23.231600Z

just less rigorous soundness xD

2020-02-22T00:12:27.231800Z

so I'm a fan

2020-02-22T00:12:33.232Z

g2g have a good night

zilti 2020-02-22T00:15:12.232200Z

bye!

2020-02-22T00:19:01.232400Z

good night!

2020-02-22T18:14:33.001500Z

I just crossed the threshold of "holy shit I'm going to have to rewrite typed clojure", and I didn't like it at all. Taking a step back to figure out how to embed what I need into our normal type syntax so then we can generate specs (eg., specify custom generators) and then I'm going to write type->spec, and spec->type transformations.

2020-02-22T18:15:09.002200Z

effectively to make generators work with polymorphic functions I needed a subtyping hierarchy for spec that exactly corresponds to typed clojure's subtyping.

2020-02-22T18:16:02.003200Z

so this is much easier: (defn subspec? [s1 s2] (subtype? (spec->type s1) (spec->type s2))

2020-02-22T18:16:09.003500Z

than rewriting typed clojure!

2020-02-22T18:16:55.004300Z

so it seems likely the 1.0 <-> 2.0 syntax will be similar for those reasons @zilti

2020-02-22T18:18:11.005300Z

also, thanks all for hanging out in this channel, it really helps to know I'm not in a vacuum

❤️ 4
2020-02-22T22:20:36.009100Z

I'm not sure if I understand above, apologies for my ignorance. I was thinking about the things you said about how hard it is to have to type check update-in and higher level functions. Is this because in theory you have to support weird things like (update-in {} [:a] update-in [:b] update-in [:c] update-in [:d] (fnil inc 0))

2020-02-22T22:22:04.010500Z

If so, my naive brain thinks a more constrained version of Clojure could be a lot easier to type check? So maybe easier to make a constrained version of Clojure?

2020-02-22T23:18:49.013200Z

@jeroenvandijk yea the nested update-in's are really really hard and interesting. And the constrained version of Clojure you're talking about is effectively the subset that core.typed <=1.0 supports. The secret weapon we have is that I spend 5 years doing a phd researching how to tackle exactly the problem of how to support a full clojure language.

2020-02-22T23:20:02.014400Z

IMO Clojure at its heart is about higher-order functions and immutable reasoning about data structures. we can't really get away from that. eg., even in babashka, all the examples use higher-order functions

2020-02-22T23:21:28.015800Z

I have a pretty good idea how to type check any combination of nested update-in's or whatever using symbolic execution and following the implicit data flow in polymorphic types

2020-02-22T23:23:14.017300Z

even a simple expression like (if (integer? x) (inc x) (str x)) is mandatory for a clojure type system to be able to check. that brings a lot of complexity (that thankfully Typed Racket already solved)

2020-02-22T23:25:04.018400Z

I personally don't think there is a subset of clojure that is particularly easier to type check (at least to the rigor that would satisfy me)

2020-02-22T23:25:29.019Z

you basically need to take away all the features that make it resemble clojure

2020-02-22T23:27:37.021200Z

if , fn , higher-order functions, immutable data structures (and reasoning about them) and macros are extremely important to clojure. and that combination is what's so hard about it.

2020-02-22T23:28:30.022Z

Thanks for explaining! I really have a poor idea of what problems you have needed to tackle and are trying to tackle

2020-02-22T23:29:32.023100Z

totally

2020-02-22T23:30:06.024Z

and it's really cool you realize that nested update-in's are hard. it took me a long time to even think of those cases

2020-02-22T23:31:22.025300Z

hehe it's only because you mentioned it I thought of that example. In that notation it is actually not super visible that it has several orders

2020-02-22T23:31:32.025600Z

xD

2020-02-22T23:32:14.026900Z

but like (update-in m [:a :b] dissoc :b) is just as hard and much more realistic

2020-02-22T23:32:24.027400Z

erm. not quite as hard. but similar solution.

2020-02-22T23:32:31.027600Z

I'm suprised that if is causing so many problems. I can imagine the higher order functions, but I have no feeling with if yet. I built a very simple type checker today and if seems not too bad. But I cannot evaluate anything so what am I saying 🙈

2020-02-22T23:33:04.028200Z

yea, think about (fn [{:keys [a] :as m}] (when a (:a m)))

2020-02-22T23:33:22.028700Z

you can use fast and loose reasoning with immutable data structures

2020-02-22T23:33:37.029Z

like knowing that a is non-nil means you also need to update m

2020-02-22T23:33:48.029200Z

that kind of stuff is pretty common

2020-02-22T23:34:04.029500Z

at least of that flavor

2020-02-22T23:35:19.030600Z

or even simpler (fn [a :- (U Str Int)] (if (integer? a) (inc a) (str/split "," a)))

2020-02-22T23:35:36.031100Z

a is Int in the then branch, but it's Str in the else

2020-02-22T23:36:01.031600Z

and you must update its type otherwise the branches won't type check

2020-02-22T23:36:14.032Z

the tech behind that is called occurrence typing

2020-02-22T23:36:45.032500Z

it basically uses propositional logic to update local variables' types

2020-02-22T23:37:25.033400Z

like at first we know a :- (U Str Int) , but we learn a :- Int in the then branch, and a :- (Not Int) in the else branch

2020-02-22T23:37:36.033900Z

and if you smash all the info together, you get the types you need

2020-02-22T23:38:11.034600Z

It sounds super complicated and I really have no clue about all this, but super interesting nonetheless!

2020-02-22T23:38:39.035Z

Is there something like just in time type checking?

2020-02-22T23:38:49.035400Z

dependent type checking, maybe?

2020-02-22T23:38:56.035700Z

that interleaves evaluation and type checking

2020-02-22T23:39:22.036500Z

I was thinking of having a sci like interpreter that does some basic type checking and gives type feedback when it comes accross incompatible types

2020-02-22T23:39:43.037300Z

there are also systems that check what it can at compile time, and then emit assertions to ensure the stuff it can't verify at runtime

2020-02-22T23:39:46.037400Z

I have the ignorance is bless advantage, I really have no clue

2020-02-22T23:40:02.037600Z

what does sci have?

2020-02-22T23:40:22.038Z

Nothing, but it made me think about type checking

2020-02-22T23:40:35.038500Z

oh I mean what features compared to clojure?

2020-02-22T23:40:44.038900Z

Ah ok

2020-02-22T23:40:47.039200Z

does it have all the features I listed?

2020-02-22T23:41:30.040100Z

I think the difference is that it is interpreted and not compiled?

2020-02-22T23:41:41.040500Z

ahk. so it's just the same language

2020-02-22T23:42:40.041700Z

That's the intention at least. It is still new so some macros are missing. And I recently added a check for the number of args to if. But all in all it's getting pretty feature complete

2020-02-22T23:43:29.042400Z

ok interesting. I'd encourage you to try your hand at type checking it.

2020-02-22T23:44:12.043200Z

I'm extremely ambitious with my type checking work

2020-02-22T23:44:23.043500Z

so it's hard for me to relate to what you need

2020-02-22T23:46:19.045700Z

I just think the bare minimum would already be of incredible value in a basic context. I'm thinking of creating a web framework where I also different experience levels. So a novice would not have access to update-in . Maybe if though :)

2020-02-22T23:46:34.046Z

Btw this is my weird approach I tried today https://gist.github.com/jeroenvandijk/aac0d4373ede06aea927840f3fd41faf

2020-02-22T23:47:16.047Z

ah nice

2020-02-22T23:47:16.047100Z

it's nothing serious, but I thought of combining type checking with the interpreter. So if it is hard to type checking just run it throught the interpreter and see what comes out (given no side efffects)

2020-02-22T23:47:57.048Z

yea this looks great

2020-02-22T23:48:00.048100Z

But looking at the code of Sci I have a long way to go 🙂 And then i might realize that i have to solve all the problems you want to solve

2020-02-22T23:48:54.049Z

I think the interesting design point on a system like yours is deciding when to warn the user.

2020-02-22T23:49:22.049700Z

Yeah indeed

2020-02-22T23:49:35.050200Z

IIRC Java has some interesting rules for when a cast is allowed

2020-02-22T23:49:45.050800Z

I would like to use the position as well. Like how clojure.spec has positions of forms

2020-02-22T23:49:56.051100Z

you can only cast to a type that is a subtype of its static type

2020-02-22T23:50:05.051300Z

you can't cast an Int to a String

2020-02-22T23:50:24.051800Z

might be remembering it wrong. but that's the gist of the tradeoff you have to make

2020-02-22T23:50:59.052500Z

so you might not warn if you're expecting Str and you get (U nil Str). But warn if you get Int. But don't warn if you get (U Str Int)

2020-02-22T23:51:01.052700Z

stuff like that

2020-02-22T23:51:24.053Z

there's probably a nice balance that can weed out many small bugs

2020-02-22T23:51:42.053400Z

so I think your approach is a great idea

2020-02-22T23:52:17.053900Z

Thanks! I hope it has some practical value later

2020-02-22T23:52:49.055100Z

"success typing" is a related area. it only throws a type error if it's guaranteed to blow up at runtime.

2020-02-22T23:52:54.055400Z

Dialyzer uses that iirc

2020-02-22T23:52:58.055500Z

I'm really happy the Sci feature of being able to blacklist/whitelist functions. That makes it possible to reduce the potential problems

2020-02-22T23:53:08.055800Z

blacklist?

2020-02-22T23:53:12.055900Z

Ah interesting

2020-02-22T23:54:05.056400Z

it has deny and allow (https://github.com/borkdude/sci#usage)

2020-02-22T23:54:35.057300Z

also it can control how many iterations it can do. To prevent endless loops for instance

2020-02-22T23:54:50.057900Z

So a nice clojure sandbox

2020-02-22T23:54:53.058100Z

oh man. now I get it. this throws away all the issues with sandboxing on the JVM

2020-02-22T23:55:02.058300Z

mind blown

2020-02-22T23:55:06.058500Z

without having to use cljs

2020-02-22T23:55:12.058900Z

yeah exactly

2020-02-22T23:55:13.059Z

even that is too much

2020-02-22T23:55:53.059800Z

Yeah I think this has many applications

2020-02-22T23:56:48.060600Z

So I want to use Sci for novices and say hey you can use the 40 function specifically for web templates

2020-02-22T23:56:54.060800Z

something like that

2020-02-22T23:57:19.061200Z

It is also a much better eval strategy than cljs had with self-hosted

2020-02-22T23:57:28.061500Z

self-hosted cljs was still a pain