roc-lang / roc

A fast, friendly, functional language.
https://roc-lang.org
Universal Permissive License v1.0
4.4k stars 309 forks source link

Implement unspecialized lambda sets in roc #3140

Closed ayazhafiz closed 2 years ago

ayazhafiz commented 2 years ago

Adding this as a tracking issue to implement unspecialized lambda sets in Roc, which I intend to do next. This should unblock some troubles we currently have during monomorphization of abilities and get us closer to an end-in-sight.

I implemented this in cor and it works so I think it should be fine in Roc too.

Some ideas after this is done:

proto thunkDefault a : () -> () -> a
#     ^^^^^^^^^^^^

let thunkDefault = \() -> \() -> T1
#   ^^^^^^^^^^^^

let useT1 = \T1 -> ()

entry test =
  let alias = thunkDefault in
  #   ^^^^^
  useT1 (alias () ())
  #      ^^^^^

is inferred as

> cor-out +solve -elab
> proto thunkDefault a : () -> () -> a
> #     ^^^^^^^^^^^^ () -[~1:a:thunkDefault]-> () -[~2:a:thunkDefault]-> a
> 
> let thunkDefault = \() -> \() -> T1
> #   ^^^^^^^^^^^^ () -[[`3]]-> () -[[`2]]-> T1
> 
> let useT1 = \T1 -> ()
> 
> entry test =
>   let alias = thunkDefault in
> #     ^^^^^ () -[[] + ~1:?13:thunkDefault]-> () -[[] + ~2:?13:thunkDefault]-> ?13
>   useT1 (alias () ())
> #        ^^^^^ () -[[`3]]-> () -[[`2]]-> T1
> 

which then monomorphizes as

> cor-out +mono -print
> let `2~1 =
>   \() -> T1
> 
> let `3(thunkDefault)~1 =
>   \() -> `2~1
> 
> let `1(useT1)~1 =
>   \T1 -> ()
> 
> entry test~1 =
>   `1(useT1)~1 (`3(thunkDefault)~1 () ())

> cor-out +eval -print
> test~1 = ()

Notice that alias was completely irrelevant here, and was eliminated during monomorphization! So we got a very cheap optimization. Today we do this elimination in Roc, but it's done syntactically - we keep a look-aside table of identifier aliases. With this technique, we can instead eliminate aliases automatically by inlining lambda set names where singleton aliases are used. To be more clear, in useT1 (alias () ()), alias was aliasing the closure with name `3, and the type of alias in that position tells us that it definitely resolves to `3, so we can fully replace alias with `3 - that means when time comes, let alias = ... can also be eliminated.

ayazhafiz commented 2 years ago

@LK this is what I was telling you about yesterday. I hope this example makes some more sense.