core-typed

Typed Clojure, an optional type system for Clojure
2020-02-14T14:34:44.001300Z

answered @zilti over here if anyone wants to know https://clojurians.slack.com/archives/C03S1KBA2/p1581690839174800

2020-02-14T14:50:46.001700Z

@zilti anything I can help with?

zilti 2020-02-14T14:57:21.005300Z

Actually I am writing annotations right now ^^ Just like when I tried it a few years ago. And yes, I have two questions... 1. How do I define a subtype just for one declaration? Like [a Any -> a] where a is a subtype of let's say (U Kw Sym) only in that one definition? 2. core.typed just told me to add type hints to a Java constructor call. Making the argument be annotated seems to not suffice, is that on purpose, or not implemented yet?

2020-02-14T14:59:45.006400Z

1. let me look up the syntax xD 2. turn on (set! **warn-on-reflection** true) and fix reflection warnings. it should stop complaining

2020-02-14T15:02:07.007Z

(All [[a :< (U Kw Sym)]] [a Any -> a])

2020-02-14T15:02:09.007200Z

try that

2020-02-14T15:03:14.008Z

its a kwargs syntax, :< defines super constraint, :> subtype constraint

zilti 2020-02-14T15:03:29.008300Z

Okay, yes, I wasn't really sure how to express it anymore

2020-02-14T15:09:13.009900Z

btw which core.typed jar/s are you using?

zilti 2020-02-14T15:09:31.010400Z

And one more to vararg functions. I have a function (fn [& selectors] ...) and the annotation [& (Vec Selector) -> [State -> State]] is apparently wrong

2020-02-14T15:09:47.010600Z

yea.. one sec

zilti 2020-02-14T15:09:53.010800Z

I am using org.clojure/core.typed.checker.jvm {:mvn/version "0.7.2"}

2020-02-14T15:10:11.011100Z

try [(Vec Selector) * -> [State -> State]]

2020-02-14T15:10:18.011400Z

cool!

zilti 2020-02-14T15:10:40.011600Z

Ooh yes, that works 🙂

2020-02-14T15:10:44.011800Z

woop!

2020-02-14T15:11:41.012200Z

there's a & syntax for kwargs. some docs here https://github.com/clojure/core.typed/wiki/Types#rest-parameters

2020-02-14T15:11:54.012500Z

except use IFn

2020-02-14T15:12:04.012900Z

sorry, woefully out of date

zilti 2020-02-14T15:12:05.013Z

Yea, that is the one I ran into, but I don't need kwargs in this case

zilti 2020-02-14T15:23:00.014500Z

Also, fyi: this will fail with an "Unannotated var: set-key" error: (defn set-key [key :- Kw modificator-fn [State -> Any]] :- [State -> State] ...) But it works when annotating it separately like this: (ann set-key [Kw [State -> Any] -> [State -> State]])

2020-02-14T15:29:28.014800Z

is that with t/defn ?

zilti 2020-02-14T15:31:35.015700Z

Yes. Actually I did a :as t :refer :all and tried both defn and t/defn. Seems to be a bit inconsistent though, so I am just using ann for now. If I find time I'll try to find out how it breaks

zilti 2020-02-14T15:31:54.016Z

All other functions in the namespace after that one had the same problem

zilti 2020-02-14T15:32:56.016400Z

That error btw happened when I tried checking with cf

2020-02-14T15:35:02.017700Z

needs an ann

2020-02-14T15:35:23.018100Z

that's by design, so it's pretty useless now

2020-02-14T15:35:43.018600Z

(core.typed used to infer a def's type based on its initial value)

2020-02-14T15:36:04.018900Z

that's one of the design points that's up in the air atm

zilti 2020-02-14T15:36:33.019500Z

I see

2020-02-14T15:36:37.019700Z

fwiw, it's about the situation when you use set-key from another namespace

2020-02-14T15:36:54.020300Z

ann just updates the global environment, so you just need to eval the namespace

zilti 2020-02-14T15:36:56.020500Z

Sounds about right

2020-02-14T15:37:14.020900Z

t/defn needs to actually type check the var => transitively type check everything

2020-02-14T15:37:34.021300Z

which was a killer for performance

2020-02-14T15:38:04.022200Z

obviously also a good idea in many situations, but you didn't have the option to choose with t/defn 's behavior

zilti 2020-02-14T15:38:08.022300Z

But isn't the point about static type checking that it is allowed to have sorta bad performance?

2020-02-14T15:38:18.022500Z

haha

2020-02-14T15:38:26.022700Z

tell that to all the users that ran away

zilti 2020-02-14T15:38:37.022900Z

Hmm

zilti 2020-02-14T15:39:06.023300Z

I guess a problem was that core.typed threw away all the info between each invocation?

zilti 2020-02-14T15:39:18.023800Z

Of course, caching and validating that would be a huge undertaking on its own

2020-02-14T15:39:24.024Z

that was one issue, that's gone now

2020-02-14T15:39:44.024400Z

that allowed transitive deps to be checked less often

2020-02-14T15:39:57.024800Z

but it didn't allow transitive deps to be omitted from checking altogether

2020-02-14T15:40:23.025300Z

I think the worry that core.typed is going to try and "check the world" is legit

2020-02-14T15:40:35.025600Z

and just literally checking one file is really compelling to me

2020-02-14T15:40:44.025900Z

and just trusting all annotations outside the ns

2020-02-14T15:41:19.026600Z

eliminates many-minute waits for initial check-ns's that don't really do anything, and can be delegated to CI

zilti 2020-02-14T15:41:39.026900Z

True, yes

zilti 2020-02-14T15:42:00.027300Z

Then again that makes it less type-safe ^^

2020-02-14T15:42:35.027800Z

yea it's a tradeoff

2020-02-14T15:42:58.028400Z

I tried uber-typesafe and I couldnt' pull it off with usable perf

2020-02-14T15:43:06.028600Z

see: first 5 years of core.tyepd

zilti 2020-02-14T15:43:13.028800Z

Yea, I remember

2020-02-14T15:43:15.029Z

haha

zilti 2020-02-14T15:44:01.030100Z

And it was bleeding into all dependencies. "Oh, you want to use obscure-ns/whatever? Please add an annotation for half the ns", which is something you just can't pull off with an optional type system

2020-02-14T15:44:25.030700Z

if you care, should be easy to make you're own wrapper for t/defn that also emits an ann by reusing the machinery around the link above ^

2020-02-14T15:44:32.030900Z

yea

2020-02-14T15:45:14.031600Z

yea, and also "oh, you use this ns? let me try and type check it too!"

2020-02-14T15:45:18.031900Z

:no-check everything

2020-02-14T15:45:22.032100Z

just a nightmare

zilti 2020-02-14T15:45:50.032800Z

I mean even now I had to add annotations for two clojure.alpha.spec functions because I have s/def in my code

2020-02-14T15:46:19.033200Z

t/tc-ignore all spec forms imo 🙂

2020-02-14T15:46:33.033600Z

should work wrapping any number of top-level forms

2020-02-14T15:46:50.033900Z

you can only give garbage annotations anyway

zilti 2020-02-14T15:48:06.035100Z

I was being a little perfectionist and gave somewhat useful annotations 😛

2020-02-14T15:48:14.035300Z

oh what did you give?

zilti 2020-02-14T15:48:45.035500Z

(ann s/register (All [[a :< (U Kw Sym)]] [a Any -> a]))

2020-02-14T15:48:54.035900Z

ahhh yes ok

zilti 2020-02-14T15:48:56.036Z

I also had to do (ann str/includes? [String String -> Bool])

2020-02-14T15:49:04.036200Z

hehe

zilti 2020-02-14T15:50:20.037600Z

Now, am I right that in the s/register example above, a can be Kw on the argument side and Sym on the return side in this case, and I'd need to make two annotations in a Union to cover that it returns Kw when given Kw?

2020-02-14T15:50:58.037900Z

what does s/register return?

2020-02-14T15:51:05.038100Z

at runtime

2020-02-14T15:51:20.038400Z

(stepping back for a sec)

zilti 2020-02-14T15:52:32.039100Z

It literally returns the argument again at the end. It is (defn register [k s] ... k)

zilti 2020-02-14T15:53:57.041700Z

I guess what I am thinking about are generics? Like in Java where you'd write "K register(K k, S s)"

2020-02-14T15:53:58.041800Z

ah. I would annotate it [(U Kw Sym) Any -> (U Kw Sym)] you only need the type variable if you're going to type-check code that uses s/register and cares exactly which keyword/sym you return.

zilti 2020-02-14T15:54:24.042600Z

Yes, I know I am being hypothetical here ^^ Just wondering.

2020-02-14T15:54:42.043Z

you can also do (IFn [Kw Any -> Kw] [Sym Any -> Sym] [(U Kw Sym) Any -> (U Kw Sym)]) if you want to maintain the dependency between first arg and return

2020-02-14T15:54:50.043200Z

but back to your original q...

zilti 2020-02-14T15:55:26.043600Z

Yea but what if k could be half a dozen types

2020-02-14T15:56:17.044600Z

ok so your case is wondering whether (ann-form (s/register :a ...) Sym) would type check using that annotation

2020-02-14T15:56:31.045Z

good question

zilti 2020-02-14T15:56:42.045200Z

I am basically wondering if there is a generics-like functionality in core.typed

2020-02-14T15:57:39.046300Z

K register(K k, S s) is (All [K S] [K S -> K])

2020-02-14T15:58:03.046700Z

erm, assuming K/S are type variables

zilti 2020-02-14T15:58:42.047600Z

Ah. So basically, my annotation is already correct in that sense, (ann s/register (All [[a :< (U Kw Sym)]] [a Any -> a])) does imply that if argument a is of type Kw the return type will also be Kw?

zilti 2020-02-14T15:59:14.048400Z

(or, of course, that the return type is a hypothetical subtype of Kw)

2020-02-14T15:59:49.049200Z

well, I'm not sure. the type information for a flows in from the type of the first argument and the expected type of the return. which is why I'm not sure if (ann-form (s/register :a ...) Sym) would be allowed

2020-02-14T16:00:35.050200Z

but I guess a would need to be (U ':a Sym)

2020-02-14T16:00:41.050500Z

which is not a subtype of Sym

2020-02-14T16:00:47.050700Z

so that would be disallowed

2020-02-14T16:01:19.051Z

it's a good question, still thinking

zilti 2020-02-14T16:02:26.052600Z

I mean, in Java it would be clear - K register (K k, S s) guarantees that the return type K is the same or a subtype of the argument K, so when I give a string, I am guaranteed that the function will return a string or a subtype of string

2020-02-14T16:03:34.053700Z

the equivalent in java fwiw is (forget the syntax) <K super (U Kw Sym)> K register (K k, S s)

2020-02-14T16:03:44.053900Z

so we're on the same page

zilti 2020-02-14T16:04:10.054100Z

Hmm right, yes

zilti 2020-02-14T16:04:40.054800Z

And there I wouldn't know either if that would guarante the returned value to be of (sub)type of the argument given

2020-02-14T16:04:58.055200Z

ok. I think you're right. ((t/inst s/register Kw) ...) is guaranteed to return a keyword

2020-02-14T16:05:14.055600Z

core.typed has to basically choose a type for a

2020-02-14T16:05:34.056Z

and you can only choose once, and a is simply substituted for the chosen type

2020-02-14T16:05:50.056400Z

I was lost in details about which type core.typed chooses

2020-02-14T16:06:30.057100Z

like, (s/register (ann-form :a (U Sym Kw)) ...) returns (U Sym Kw)

2020-02-14T16:06:46.057400Z

sorry, haven't thought about this stuff in a while

zilti 2020-02-14T16:06:59.057700Z

Ahh. Good, yes, that is what I was hoping for

2020-02-14T16:07:18.058200Z

great

zilti 2020-02-14T16:07:19.058300Z

It has been a long time that I spent time thinking about types, I gotta admit

2020-02-14T16:07:30.058500Z

😛

2020-02-14T16:09:58.059500Z

g2g nice chatting about types again! I get emailed about messages in this channel and I usually respond within 24hrs, or try the core.typed google group

zilti 2020-02-14T16:10:19.059900Z

Okay, see you! And thanks for your time and work!

2020-02-14T16:11:09.060100Z

cheers!

whilo 2020-02-14T19:16:21.060600Z

@ambrosebs hey! nice that you continue working on core.typed!

whilo 2020-02-14T19:16:56.061Z

do you plan to port https://docs.racket-lang.org/turnstile/index.html to clojure?