-
-
Notifications
You must be signed in to change notification settings - Fork 315
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Thunks that resolve to procs fail to preserve specialization information #4734
Comments
Looks like another lambda set resolution bug. #4745 does not cut this one down. Needs a minimal repro |
minimization:
|
The elaboration for
is
which appears to be correct. There is a missing specialization
but there is one present, where the diff is ❯ diff a.d b.d
6c6
< ( Test.15, []),
---
> ( Test.14, []),
so, we are missing a specialization of |
Okay, so the problem here is that In particular, we have two calls to
but, since
We might think that somehow the nested captures of
now there are two
but again, we will only wind up storing |
@shritesh as a workaround for now, this should work if you explicitly wrap the
|
An "eta-reduced thunk" is a top-level, non-capturing value (compiled as a thunk) that returns a specialized procedure. Had the value been eta-expanded, it would be a function that returns another function. Because 1. specialized functions are identified by their [function signature][RawFunctionLayout], consisting of arguments, lambda set, and return layout 2. the runtime representation of a function is only its lambda set 3. top-level, non-capturing values are identified only by their runtime representation eta-reduced thunks lose information about the specialized procedures they resolve to, as their layout identification includes only the lambda set of the returned procedure, but that returned procedure is fully identified by its signature as described in (1). To recover this information, eta-reduced thunks are installed a niche that is the [function signature][RawFunctionLayout] of the specialized procedure they return. **Example** Consider ```ignore(illustrative) andThen = \{} -> x = 10 \newFn -[5]-> Num.add (newFn {}) x between = andThen {} it = between \{} -[7]-> between \{} -[8]-> 10 main = it ``` Here `between` is an eta-reduced thunk, for which we want two specializations ```ignore(illustrative) between : ({} -[[7]]-> U8) -[[5 U8]]-> U8 between : ({} -[[8]]-> U8) -[[5 U8]]-> U8 ``` Since `between` always resolves to the function `-[5]->`, its layout identification is ```ignore(illustrative) {} -> [[5 U8]] ``` But, the construction of that returned lambda set `[[5 U8]]` is different between the two specializations. To distinguish those differences, we attach the returned functions' layout as a niche to each specialization of the thunk. ```ignore(illustrative) between spec 1: {} -> [[5 U8]] (niche: η({} -[[7]]-> U8) -[[5 U8]]-> U8) between spec 1: {} -> [[5 U8]] (niche: η({} -[[8]]-> U8) -[[5 U8]]-> U8) ``` **Composing with multimorphic lambda sets** Eta-reduced thunk niches compose with specializations involved in multimorphic lambda sets naturally, because the two concepts are disjoint. Consider ```ignore(illustrative) andThen = \x -> \newFn -[5]-> Str.concat (newFn {}) (Num.toStr x) between = if Bool.true then andThen 11u8 else andThen 22u16 it = between \{} -[7]-> between \{} -[8]-> "end" main = it ``` For this program, we want two specializations of `between` ```ignore(illustrative) between : ({} -[[7]]-> Str) -[[5 U16, 5 U8]]-> Str between : ({} -[[8]]-> Str) -[[5 U16, 5 U8]]-> Str ``` It is again enough to specify that each `between` thunk returns a closure data of lambda set shape `[[5 U16, 5 U8]]`, with an appropriate eta-reduced thunk niche. The thunk itself does not capture, and the disambiguation of procedures in the multimorphic lambda set is only relevant at the site of the construction of the lambda set (and its dispatch). That is, the niches needed to distinguish the constructed procedures in the lambda set are only relevant - **inside** the body of `between` (specifically, inside the specialization of `andThen`) - OR, **outside** the body of `between`, when matching on it to make the calls in `it` And so, nothing extra is needed on the surface of the eta-reduced thunk's niche to identify this program correctly. **References** See also #4734
An "eta-reduced thunk" is a top-level, non-capturing value (compiled as a thunk) that returns a specialized procedure. Had the value been eta-expanded, it would be a function that returns another function. Because 1. specialized functions are identified by their [function signature][RawFunctionLayout], consisting of arguments, lambda set, and return layout 2. the runtime representation of a function is only its lambda set 3. top-level, non-capturing values are identified only by their runtime representation eta-reduced thunks lose information about the specialized procedures they resolve to, as their layout identification includes only the lambda set of the returned procedure, but that returned procedure is fully identified by its signature as described in (1). To recover this information, eta-reduced thunks are installed a niche that is the [function signature][RawFunctionLayout] of the specialized procedure they return. **Example** Consider ```ignore(illustrative) andThen = \{} -> x = 10 \newFn -[5]-> Num.add (newFn {}) x between = andThen {} it = between \{} -[7]-> between \{} -[8]-> 10 main = it ``` Here `between` is an eta-reduced thunk, for which we want two specializations ```ignore(illustrative) between : ({} -[[7]]-> U8) -[[5 U8]]-> U8 between : ({} -[[8]]-> U8) -[[5 U8]]-> U8 ``` Since `between` always resolves to the function `-[5]->`, its layout identification is ```ignore(illustrative) {} -> [[5 U8]] ``` But, the construction of that returned lambda set `[[5 U8]]` is different between the two specializations. To distinguish those differences, we attach the returned functions' layout as a niche to each specialization of the thunk. ```ignore(illustrative) between spec 1: {} -> [[5 U8]] (niche: η({} -[[7]]-> U8) -[[5 U8]]-> U8) between spec 1: {} -> [[5 U8]] (niche: η({} -[[8]]-> U8) -[[5 U8]]-> U8) ``` **Composing with multimorphic lambda sets** Eta-reduced thunk niches compose with specializations involved in multimorphic lambda sets naturally, because the two concepts are disjoint. Consider ```ignore(illustrative) andThen = \x -> \newFn -[5]-> Str.concat (newFn {}) (Num.toStr x) between = if Bool.true then andThen 11u8 else andThen 22u16 it = between \{} -[7]-> between \{} -[8]-> "end" main = it ``` For this program, we want two specializations of `between` ```ignore(illustrative) between : ({} -[[7]]-> Str) -[[5 U16, 5 U8]]-> Str between : ({} -[[8]]-> Str) -[[5 U16, 5 U8]]-> Str ``` It is again enough to specify that each `between` thunk returns a closure data of lambda set shape `[[5 U16, 5 U8]]`, with an appropriate eta-reduced thunk niche. The thunk itself does not capture, and the disambiguation of procedures in the multimorphic lambda set is only relevant at the site of the construction of the lambda set (and its dispatch). That is, the niches needed to distinguish the constructed procedures in the lambda set are only relevant - **inside** the body of `between` (specifically, inside the specialization of `andThen`) - OR, **outside** the body of `between`, when matching on it to make the calls in `it` And so, nothing extra is needed on the surface of the eta-reduced thunk's niche to identify this program correctly. **References** See also #4734
I'm writing a Random Number Generator and wanted to have an easy way to compose generators, so I implemented the following
andThen
function, that takes in an existing generator, it's init and a "mapping function" for the new generator. This typechecks but panics during codegen.https://github.com/shritesh/raytrace.roc/blob/shritesh/broken-generator/RNG.roc#L5-L39
The panic message is:
The text was updated successfully, but these errors were encountered: