Lysxia / first-class-families

First-class type families
https://hackage.haskell.org/package/first-class-families
MIT License
86 stars 12 forks source link

PR for Unfoldr, Concat, ConcatMap and Replicate #16

Closed gspia closed 4 years ago

gspia commented 4 years ago

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.

Lysxia commented 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))
gspia commented 4 years ago

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 types, 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.)

Lysxia commented 4 years ago

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.

gspia commented 4 years ago
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

Lysxia commented 4 years ago

Thank you!