Replies: 5 comments 6 replies
-
Sorry for not giving a response right away. Still need to process the idea to respond with continuation. |
Beta Was this translation helpful? Give feedback.
-
This part is wrong, We do rightfully have thunks into thunks, when we pass a thunk as a function argument. All the funtion arguments are thunked by default, creating a thunk stack. It is how the code works. |
Beta Was this translation helpful? Give feedback.
-
If we compare with nix, we find a clever optimisation where thunking only happens on complex expressions https://github.com/NixOS/nix/blob/fb0ad898ed0f1c8d5297f5dc1035b2e6ac7e3632/src/libexpr/eval.cc#L680-L726 We cannot do that in our |
Beta Was this translation helpful? Give feedback.
-
As I understood type system was deliberately was done that polymorphic & open. The strictness analysis so far was not done & it is not that long to do it, I just want to postpone it until it would be needed, so It would be good if we would be able to distinguish between WHNFs & not, but going that route may complicate the multithreading, I am talking about that one that Obsidian guys almost provided, I do not know thou precisely, it should be looked into. Please, lets stick with |
Beta Was this translation helpful? Give feedback.
-
Ok. Part of it I already clearly understand. Indeed:
Agree. It seems to be the next logical step. |
Beta Was this translation helpful? Give feedback.
-
At the moment there remains at least two different values representation in HNix, and the ambiguity is killing performance and any ability to refactor or otherwise tune execution of the code.
The confusion arises because we mix all the hnix contexts (monads). There is an haskell execution context (think IO) and a nix execution context (think Context). But they are indistinguishable ! It appears clearly in the
thunk :: m v -> m v
signature, where we wrap an action into a value. We would expect it to bem v -> v
, but the thunk mechanism itself relies on IO. It would make sense to distinguish these contexts. What aboutevalM whnf -> execM v
?This raises a second puzzle for me: There is no way to tell thunks from WHNF values. A generic value can be either, but in many cases we know more than that. For example
demand :: m v -> m v
gives not information at all to the type system. But we know that the result of demand cannot be a thunk, so we could type itdeamnd :: m v -> m v'
where v' is the type of WHNF values (an NValue' in most cases).I did that in a branch, and it works, with a negligible performance loss.
The implementation of
demand
is also a puzzle. OnPure t
values, it isdemand =<< force t
. We see that we force the thunk, and continue demanding values as long as the thunk evaluation yields another thunk.We see that there are two levels of thunks. Thunks at the NValue level (Pure t) and thunks at the MonadThunk level (t). Having both defeats the purpose of locking thunks, because the chain of thunks being forced is interrupted, and thus the loop detection mechanism aborted.
My experiment discovered that it does not happen because
eval
ensures it does not happen (once in all the test suite, possibly a bug to be discovered).The experiment removed a single
demand
ineval
and suddenly there were inifinite evaluations in some tests testing the absence of divergence. Because we are not backed by the types, we can miss invocations ofdemand
.The experiment also showed that we can drop some invocations of demand too. At some place, we
demand
things that cannot be thunks.My point is that we are not leveraging the types to convey enough information about our values. I guess it results from the history of the project.
Or because or types are already quite convoluted ? (think
forall e t f m.
)Beta Was this translation helpful? Give feedback.
All reactions