Closed gspia closed 4 years ago
Thanks a lot for this!
Unfoldr can be factored further by noticing that (f @@ c) is being evaluated twice, under IsJust and UnfoldrCase. Only one is needed:
type instance Eval (Unfoldr f c) = Eval (UnfoldrCase f (f @@ c))
In UnfoldrCase, use Fst and Snd instead of pattern-matching: this makes the function "lazier" and unstuck more often.
In NumIter, (Eval (If x (Pure y) (Pure z))) is pretty much equivalent to the simpler (If x y z). In both versions, all three of x, y, z will be evaluated. The point of fcf in this context is to introduce a layer of indirection to explicitly control when evaluation happens, but using Eval inside y or z (as is done in the current definition of Unfoldr for instance) defeats that point. (Pure (Eval w)) can be simplified to w, with the benefit that the computation of w remains delayed (because there is no more (Eval w)). (You are right to remark in the other discussion that this is not all obvious. I'm still unsure what's a good way to explain how to write and reason about fcfs.)
Also make NumIter count down to 0 (instead of up to n) to reduce the number of arguments.
The examples look okay. Does doctest work with ":kind!" commands and multiline definitions?
Concat, ConcatMap, and Replicate should be data (i.e., defined as fcfs), for consistency with the rest. This also provides a natural place to specify their kinds.
I prefer to bump the version number later, when making the release.
I'll have a few stylistic comments once this stabilizes.
Unfoldr can be factored further by noticing that (f @@ c) is being evaluated twice, under IsJust and UnfoldrCase. Only one is needed:
Thanks & done
type instance Eval (Unfoldr f c) = Eval (UnfoldrCase f (f @@ c))
Done
- In UnfoldrCase, use Fst and Snd instead of pattern-matching: this makes the function "lazier" and unstuck more often.
Done. I had to add Evals in there to get it compile. Is this still a bit more "lazy" than the original pattern matching version?
In NumIter, (Eval (If x (Pure y) (Pure z))) is pretty much equivalent to the simpler (If x y z).
Ok, changed that and also simplified the example at the Unfoldr
.
Also make NumIter count down to 0 (instead of up to n) to reduce the
Done.
The examples look okay. Does doctest work with ":kind!" commands and multiline definitions?
I actually don't know and cannot test it easily. Thus removed the >>>
I prefer to bump the version number later, when making the release.
Ok, changed it back to original
Concat, ConcatMap, and Replicate should be data (i.e., defined as fcfs), for consistency with the rest. This also provides a natural place to specify their kinds.
Ok, changed them to data.
A question about using directly the type vs data-definition as I just noticed that now it would be possible to also use type Unfoldr f c = UnfoldrCase f (f @@ c)
without the data-definition. Do they behave behave similarly with respect to computations? E.g. is there less stuck computations with the data-definition?
In the module, the Elem
, LookUp
and Zip
functions are just type
s, too. This is (partly) the reason I thought it would be ok to use those. Somehow reasoned that if there is no need for data, then the shorter definition would be better. But actually now while thinking about this, as you said, the data specifies (and documents) nicely the kind (which is helpful).
Is the use of type
required in any of the Elem
, LookUp
or Zip
functions?
I'll have a few stylistic comments once this stabilizes.
Ok, waiting for your comments! Noticed that the List module used two spaces for intending lines and changed these to use that too (and the example)
(Next I'll try to get the updates to my fork - should be done now - and then to your repo. Hmm, it looks like it forwarded the new commit already, nice.)
I had to add Evals in there to get it compile. Is this still a bit more "lazy" than the original pattern matching version?
Yeah that looks right.
Is the use of type required in any of the Elem, LookUp or Zip functions?
In hindsight they should probably have been data
.
Do they behave behave similarly with respect to computations? E.g. is there less stuck computations with the data-definition?
The main difference is that data
can be partially applied. The other difference is that synonyms are fragile and may lead to more confusing error messages (but that is pure speculation on my part).
(Next I'll try to get the updates to my fork - should be done now - and then to your repo. Hmm, it looks like it forwarded the new commit already, nice.)
Right! PRs just track the branch in your repo, so there's no extra step.
Is the use of type required in any of the Elem, LookUp or Zip functions?
In hindsight they should probably have been data.
Do they behave behave similarly with respect to computations? E.g. is there less stuck computations with the data-definition?
The main difference is that data can be partially applied. The other difference is that synonyms are fragile and may lead to more confusing error messages (but that is pure speculation on my part).
Thanks for your answers & comments! I think that I read somewhere that the data-definitions used only for type-level functions can be partially applied, but had already forgotten that. This is something that I think could be elaborated a bit more already in readme.md of the repo: in a way, to be able to partially apply type-level functions changes the game a bit and makes it a way more flexible for the user.
And I think that it might be worth to change the Elem
, LookUp
or Zip
functions at the same time to use data. I'll open an issue about those changes
Thank you!
Hi
Here you may find implementation for the Unfoldr, Concat, ConcatMap and Replicate. I added some examples into the docs and updated the version number as well.
And thanks for the suggestion to check the Unfoldr definition. This one is much better than what I used earlier.