Does a one way version of Coercible exist? For example if I have
newtype Prime = MkUnsafePrime Int
mkPrime :: Int -> Maybe Prime
Then I want something like instance CoercibleOneWay Prime Int with coerceOneWay :: CoercibleOneWay a b -> a -> b. With Coercible I either get both directions (if the constructor is in scope) or neither direction (if it’s not). Surely we can do better. Has anyone done something like that yet?
I think that’s what witch is all about. For converting primes to ints you could make an instance From Int Prime, and it also has a TryFrom class that you could create a TryFrom Prime Int instance for.
Sure, but the problem in this case is that I can’t bring the Coercible instance into scope (i.e. I can’t export the constructor) because that’s unsafe. It’s not valid to coerce from an Int to a Prime!
My question is whether there exists a more refined notion of Coercible that only works in one direction.
When I want to constrain coerce, I usually resort to an auxiliary type family to guide the conversion. But I’m unsure about how to do it for newtypes in general
Exporting a function to do the conversion is not enough to be zero cost, regardless of whether its inlined, because you don’t want to just be able convert a single Prime to an Int, but containers of Primes to a containers of Ints, functions returning containers of Primes to functions returning containers of Ints etc. . That’s the point of Coercible.
I think it’s a perfectly sensible feature request, but doing it “properly” would require a nontrivial amount of surgery to Core’s coercion system and GHC’s constraint solver (basically removing the symmetry constructor and making sure that all proof rules have symmetric forms where appropriate). One could of course experiment with doing it in a type-checker plugin.
Thanks Adam, that’s very helpful. I can get a long way at the library level by wrapping Coercible but ultimately I’ll need to deduce (Sub a' a, Sub b b') => Sub (D a b) (D a' b') for things like data D a b = D (a -> b), so I suspect I will eventually need compiler support (maybe TH would be good enough).
One-way coercibles would also be useful for modelling the subtype relationship between UnliftedType and Type. The way data-elevator is unsafe (also) because GHC is free to move around coercions, which it wouldn’t if they were unidirectional.
That’s a really cool library! And likely to help with the OP as well. But it doesn’t really help with my use case above because the coercion is heterogenous, i.e., it goes from one levity to another (nasty stuff).