add unwrapF and unF
fixes #10
This came up in slack just now. I mentioned that Invariant (imap unwrap wrap) might make more sense as the constraint because this is still applicable to contravariant functors. But having said that; we rarely seem to use Invariant, so perhaps having multiple declarations would be better - one for Functor and one for Contravariant?
I see this unwrapF useful for structures which contain multiple values: Array, List, some other Tree structures, as otherwise you would need to iterate on all values and apply id to each of them.
All this are Functors. As I understand most Contravariant functors have a function underneath so f a "consumes" values of type a and usually they consume just one a so it's not that costly to cmap unwrap when you are going to run the unwrap on one value. I might be missing something tho, but if not, it means the unwrap specialized for Contravariant will be not that useful.
If you think it's worth adding tho what you think about the names? are this fine: unwrapCF and unCF ?
Since we're implementing this as a straight coerce, we could drop the constraint entirely too?
If we drop the constraint than it might be unsafe
How would it be unsafe?
Yes, it actually seems to be safe even for contravention functions
There is no telling what a parameter is used for in the general case. Many libraries use unsafeCoerce themselves under the guard of carefully constructed types. The existence of wrapF and unwrapF without the Functor constraint means anyone can reach inside any type and wreak havoc.
@eric-corumdigital there is wrap defined as part of class Newtype, but there is nowrapF. this pr is proposing addition of unwrapF an unF (note this functions have Newtype constraint). It should work fine for bot functors and contravariant functors. Can you point out some example where current definition of unWrapF can cause some issues?
Yes, it works fine for Functors, but wrapF and unwrapF are not constrained to Functor, which is why there is a problem.
.... which is why there is a problem.
But what exactly is a problem? Can you point out some concrete example where the problems of unWrapF (as it's defined now) are ilustrated?
@safareli Data.Set.Set.
Now that we have safe coercions we could, in the spirit of https://github.com/purescript/purescript-newtype/pull/22, constrain unwrapF and unF with Coercible (f t) (f a) instead of Functor f and support both covariant and contravariant functors!
This would make the Newtype t a constraint redundant but we probably want to keep it to aid inference. Ideally we would have a Representational f constraint (given type Representational f = forall a b. Coercible (f a) (f b)) instead of Coercible (f t) (f a) but this requires constraint kinds and quantified constraints, we’re not there yet.
#22 was merged, so this can move forward.
Is this needed now that we have Coercible? I’d suggest letting people use coerce directly and being done with it.
Those functions could benefit from the functional dependency brought by the Newtype constraint, I’d be inclined to revisit this after 0.14 though: perhaps bare coerce will be enough for most cases and perhaps we’ll discover more helpful combinators for the rest.
I've addressed the merge conflict. Are we still interested in adding this?
CI is now failing with:
[1/1 NoInstanceFound] src/Data/Newtype.purs:62:11
62 unwrapF = coerce
^^^^^^
No type class instance was found for
Prim.Coerce.Coercible (f0 t1)
(f0 a2)
while checking that type forall (a :: Type) (b :: Type). Coercible @Type a b => a -> b
is at least as general as type f0 t1 -> f0 a2
while checking that expression coerce
has type f0 t1 -> f0 a2
in value declaration unwrapF
where f0 is a rigid type variable
bound at (line 62, column 11 - line 62, column 17)
t1 is a rigid type variable
bound at (line 62, column 11 - line 62, column 17)
a2 is a rigid type variable
bound at (line 62, column 11 - line 62, column 17)
Src Lib All
Warnings 0 0 0
Errors 1 0 1