Skip to content
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

Why does hoistFree have such a strict type? #212

Open
Wheatwizard opened this issue Jan 24, 2022 · 2 comments
Open

Why does hoistFree have such a strict type? #212

Wheatwizard opened this issue Jan 24, 2022 · 2 comments

Comments

@Wheatwizard
Copy link

Currently the type for hoistFree is:

hoistFree :: Functor g => (forall a. f a -> g a) -> Free f b -> Free g b

However this type is more restrictive than it has to be. The type inferred by ghc is:

hoistFree :: Functor g => (f (Free f a) -> g (Free f a)) -> Free f a -> Free g a

This has a looser requirement on the function. Every function that is of type forall a. f a -> g a will be of type f (Free f a) -> g (Free f a). And thus this second type is more general. We can do stuff like hoistFree sort where sort only works for a which have an Ord instance.

The only benefit I can see to the existing type is that it is more category theoretic. The documentation calls the input function a natural transformation, and indeed the type requires it be a natural transformation. The looser type always preserves natural transformations but does not require the input to be a natural transformation.

What is the reason for the type of hoistFree?

@RyanGlScott
Copy link
Collaborator

Similar generalizations have been proposed in the past, such as in #131. However, it's not clear that changing the type signature as proposed would be a clear win. See #131 (comment), which explains that hoistCofree's argument being a natural transformation makes hoistCofree a homomorphism. The same reasoning applies to hoistFree as well. If the argument's type were changed, then hoistFree would no longer be a homomorphism, which makes it more difficult to reason with.

The documentation for hoistFree and hoistCofree could be clarified to mention this more explicitly.

@Wheatwizard
Copy link
Author

I think that documentation would definitely go a good way for this function. Would it maybe be a good idea to also include the two generalizations of hoistFree:

gen1 :: Functor g => (f (Free f a) -> g (Free f a)) -> Free f a -> Free g a
gen2 :: Functor f => (f (Free g a) -> g (Free g a)) -> Free f a -> Free g a

Both of these, at least for me, are much more useful to me from a practical standpoint than hoistFree is in its current state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants