Overlapping instances - how to circumvent them - haskell

I am a beginner at Haskell, so please be indulgent. For reasons that are not important here, I am trying to define a operator <^> that takes a function and an argument and returns the value of the function by the argument, irrespective of which of the function and the argument came first. In short, I would like to be able to write the following:
foo :: Int -> Int
foo x = x * x
arg :: Int
arg = 2
foo <^> arg -- valid, returns 4
arg <^> foo -- valid, returns 4
I have tried to accomplish that through type families, as follows:
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeFamilies, TypeOperators #-}
class Combine t1 t2 where
type Output t1 t2 :: *
(<^>) :: t1 -> t2 -> Output t1 t2
instance Combine (a->b) a where
type Output (a->b) a = b
f <^> x = f x
instance Combine a (a->b) where
type Output a a->b = b
x <^> f = f x
On this code, GHC throws a Conflicting family instance declarations. My guess is that the overlap GHC complains about occurs when type a->b and type a are the same. I don't know Haskell well enough, but I suspect that with recursive type definitions, one may be able to construct such a situation. I have a couple of questions:
Since this is a rather remote scenario that will never occur in my application (in particular not with foo and arg above), I was wondering if there was a way of specifying a dummy default instance to use in case of overlap? I have tried the different OVERLAPS and OVERLAPPING flags, but they didn't have any effect.
If not, is there a better way of achieving what I want?
Thanks!

This is a bad idea, in my view, but I'll play along.
A possible solution is to switch to functional dependencies. Usually I tend to avoid fundeps in favor of type families, but here they make the instances compile in a simple way.
class Combine t1 t2 r | t1 t2 -> r where
(<^>) :: t1 -> t2 -> r
instance Combine (a->b) a b where
f <^> x = f x
instance Combine a (a->b) b where
x <^> f = f x
Note that this class will likely cause problems during type inference if we use polymorphic functions. This is because, with polymorphic functions, the code can easily become ambiguous.
For instance id <^> id could pick any of the two instances. Above, melpomene already reported const <^> id being ambiguous as well.
The following is weakly related, but I want to share it anyway:
What about type families instead? I tried to experiment a bit, and I just discovered a limitation which I did not know. Consider the closed type family
type family Output a b where
Output (a->b) a = b
Output a (a->b) = b
The code above compiles, but then the type Output a (a->b) is stuck. The second equation does not get applied, as if the first one could potentially match.
Usually, I can understand this in some other scenarios, but here unifying
Output (a' -> b') b' ~ Output a (a -> b)
seems to fail since we would need a ~ (a' -> b') ~ (a' -> a -> b) which is impossible, with finite types. For some reason, GHC does not use this argument (does it pretend infinite types exist in this check? why?)
Anyway, this makes replacing fundeps with type families harder than it could be, it seems. I have no idea about why GHC accepts the fundeps code I posted, yet refuses the OP's code which is essentially the same thing, except using type families.

#chi is close; an approach using either FunDeps or Closed Type Families is possible. But the Combine instances are potentially ambiguous/unifiable just as much as the CTF Output equations.
When chi says the FunDep code is accepted, that's only half-true: GHC plain leads you down the garden path. It will accept the instances but then you find you can't use them/you get weird error messages. See the Users Guide at "potential for overlap".
If you're looking to resolve a potentially ambiguous Combine constraint, you might get an error suggesting you try IncoherentInstances (or INCOHERENT pragma). Don't do that. You have a genuinely incoherent problem; all that will do is defer the problem to somewhere else. It's always possible to avoid Incoherent -- providing you can rejig your instances (as follows) and they're not locked away in libraries.
Notice that because of the potential ambiguity, another Haskell compiler (Hugs) doesn't let you write Combine like that. It has a more correct implementation of Haskell's (not-well-stated) rules.
The answer is to use a sort of overlap where one instance is strictly more specific. You must first decide which you way you want to prefer in case of ambiguity. I'll choose function prefixed to argument:
{-# LANGUAGE UndecidableInstances, TypeFamilies #-}
instance {-# OVERLAPPING #-} (r ~ b)
=> Combine (a->b) a r where ...
instance {-# OVERLAPPABLE #-} (Combine2 t1 t2 r)
=> Combine t1 t2 r where
(<^>) = revApp
class Combine2 t1 t2 r | t1 t2 -> r where
revApp :: t1 -> t2 -> r
instance (b ~ r) => Combine2 a (a->b) r where
revApp x f = f x
Notice that the OVERLAPPABLE instance for Combine has bare tyvars, it's a catch-all so it's always matchable. All the compiler has to do is decide whether some wanted constraint is of the form of the OVERLAPPING instance.
The Combine2 constraint on the OVERLAPPABLE instance is no smaller than the head, so you need UndecidableInstances. Also beware that deferring to Combine2 will mean that if the compiler still can't resolve, you're likely to get puzzling error messages.
Talking of bare tyvars/"always matchable", I've used an additional trick to make the compiler work really hard to improve the types: There's bare r in the head of the instance, with an Equality type improvement constraint (b ~ r) =>. To use the ~, you need to switch on TypeFamilies even though you're not writing any type families.
A CTF approach would be similar. You need a catch-all equation on Output that calls an auxiliary type function. Again you need UndecidableInstances.

Related

Why does this code using UndecidableInstances compile, then generate a runtime infinite loop?

When writing some code using UndecidableInstances earlier, I ran into something that I found very odd. I managed to unintentionally create some code that typechecks when I believed it should not:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
data Foo = Foo
class ConvertFoo a b where
convertFoo :: a -> b
instance (ConvertFoo a Foo, ConvertFoo Foo b) => ConvertFoo a b where
convertFoo = convertFoo . (convertFoo :: a -> Foo)
evil :: Int -> String
evil = convertFoo
Specifically, the convertFoo function typechecks when given any input to produce any output, as demonstrated by the evil function. At first, I thought that perhaps I managed to accidentally implement unsafeCoerce, but that is not quite true: actually attempting to call my convertFoo function (by doing something like evil 3, for example) simply goes into an infinite loop.
I sort of understand what’s going on in a vague sense. My understanding of the problem is something like this:
The instance of ConvertFoo that I have defined works on any input and output, a and b, only limited by the two additional constraints that conversion functions must exist from a -> Foo and Foo -> b.
Somehow, that definition is able to match any input and output types, so it would seem that the call to convertFoo :: a -> Foo is picking the very definition of convertFoo itself, since it’s the only one available, anyway.
Since convertFoo calls itself infinitely, the function goes into an infinite loop that never terminates.
Since convertFoo never terminates, the type of that definition is bottom, so technically none of the types are ever violated, and the program typechecks.
Now, even if the above understanding is correct, I am still confused about why the whole program typechecks. Specifically, I would expect the ConvertFoo a Foo and ConvertFoo Foo b constraints to fail, given that no such instances exist.
I do understand (at least fuzzily) that constraints don’t matter when picking an instance—the instance is picked based solely on the instance head, then constraints are checked—so I could see that those constraints might resolve just fine because of my ConvertFoo a b instance, which is about as permissive as it can possibly be. However, that would then require the same set of constraints to be resolved, which I think would put the typechecker into an infinite loop, causing GHC to either hang on compilation or give me a stack overflow error (the latter of which I’ve seen before).
Clearly, though, the typechecker does not loop, because it happily bottoms out and compiles my code happily. Why? How is the instance context satisfied in this particular example? Why doesn’t this give me a type error or produce a typechecking loop?
I wholeheartedly agree that this is a great question. It speaks to how
our intuitions about typeclasses differ from the reality.
Typeclass surprise
To see what is going on here, going to raise the stakes on the
type signature for evil:
data X
class Convert a b where
convert :: a -> b
instance (Convert a X, Convert X b) => Convert a b where
convert = convert . (convert :: a -> X)
evil :: a -> b
evil = convert
Clearly the Covert a b instance is being chosen as there is only
one instance of this class. The typechecker is thinking something like
this:
Convert a X is true if...
Convert a X is true [true by assumption]
and Convert X X is true
Convert X X is true if...
Convert X X is true [true by assumption]
Convert X X is true [true by assumption]
Convert X b is true if...
Convert X X is true [true from above]
and Convert X b is true [true by assumption]
The typechecker has surprised us. We do not expect Convert X X to be
true as we have not defined anything like it. But (Convert X X, Convert X X) => Convert X X is a kind of tautology: it is
automatically true and it is true no matter what methods are defined in the class.
This might not match our mental model of typeclasses. We expect the
compiler to gawk at this point and complain about how Convert X X
cannot be true because we have defined no instance for it. We expect
the compiler to stand at the Convert X X, to look for another spot
to walk to where Convert X X is true, and to give up because there
is no other spot where that is true. But the compiler is able to
recurse! Recurse, loop, and be Turing-complete.
We blessed the typechecker with this capability, and we did it with
UndecidableInstances. When the documentation states that it is
possible to send the compiler into a loop it is easy to assume the
worst and we assumed that the bad loops are always infinite loops. But
here we have demonstrated a loop even deadlier, a loop that
terminates – except in a surprising way.
(This is demonstrated even more starkly in Daniel's comment:
class Loop a where
loop :: a
instance Loop a => Loop a where
loop = loop
.)
The perils of undecidability
This is the exact sort of situation that UndecidableInstances
allows. If we turn that extension off and turn FlexibleContexts on
(a harmless extension that is just syntactic in nature), we get a
warning about a violation of one of the Paterson
conditions:
...
Constraint is no smaller than the instance head
in the constraint: Convert a X
(Use UndecidableInstances to permit this)
In the instance declaration for ‘Convert a b’
...
Constraint is no smaller than the instance head
in the constraint: Convert X b
(Use UndecidableInstances to permit this)
In the instance declaration for ‘Convert a b’
"No smaller than instance head," although we can mentally rewrite it
as "it is possible this instance will be used to prove an assertion of
itself and cause you much anguish and gnashing and typing." The
Paterson conditions together prevent looping in instance resolution.
Our violation here demonstrates why they are necessary, and we can
presumably consult some paper to see why they are sufficient.
Bottoming out
As for why the program at runtime infinite loops: There is the boring
answer, where evil :: a -> b cannot but infinite loop or throw an
exception or generally bottom out because we trust the Haskell
typechecker and there is no value that can inhabit a -> b except
bottom.
A more interesting answer is that, since Convert X X is
tautologically true, its instance definition is this infinite loop
convertXX :: X -> X
convertXX = convertXX . convertXX
We can similarly expand out the Convert A B instance definition.
convertAB :: A -> B
convertAB =
convertXB . convertAX
where
convertAX = convertXX . convertAX
convertXX = convertXX . convertXX
convertXB = convertXB . convertXX
This surprising behavior, and how constrained instance resolution (by
default without extensions) is meant to be as to avoid these
behaviors, perhaps can be taken as a good reason for why Haskell's
typeclass system has yet to pick up wide adoption. Despite its
impressive popularity and power, there are odd corners to it (whether
it is in documentation or error messages or syntax or maybe even in
its underlying logic) that seem particularly ill fit to how we humans
think about type-level abstractions.
Here's how I mentally process these cases:
class ConvertFoo a b where convertFoo :: a -> b
instance (ConvertFoo a Foo, ConvertFoo Foo b) => ConvertFoo a b where
convertFoo = ...
evil :: Int -> String
evil = convertFoo
First, we start by computing the set of required instances.
evil directly requires ConvertFoo Int String (1).
Then, (1) requires ConvertFoo Int Foo (2) and ConvertFoo Foo String (3).
Then, (2) requires ConvertFoo Int Foo (we already counted this) and ConvertFoo Foo Foo (4).
Then (3) requires ConvertFoo Foo Foo (counted) and ConvertFoo Foo String (counted).
Then (4) requires ConvertFoo Foo Foo (counted) and ConvertFoo Foo Foo (counted).
Hence, we reach a fixed point, which is a finite set of required instances. The compiler has no trouble with computing that set in finite time: just apply the instance definitions until no more constraint is needed.
Then, we proceed to provide the code for those instances. Here it is.
convertFoo_1 :: Int -> String
convertFoo_1 = convertFoo_3 . convertFoo_2
convertFoo_2 :: Int -> Foo
convertFoo_2 = convertFoo_4 . convertFoo_2
convertFoo_3 :: Foo -> String
convertFoo_3 = convertFoo_3 . convertFoo_4
convertFoo_4 :: Foo -> Foo
convertFoo_4 = convertFoo_4 . convertFoo_4
We get a bunch of mutually recursive instance definitions. These, in this case, will loop at runtime, but there's no reason to reject them at compile time.

Strange program that requires incoherent instances yet always seems to pick the "right" one?

Consider the following program, which only compiles with incoherent instances enabled:
{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, FlexibleInstances #-}
{-# LANGUAGE IncoherentInstances #-}
main = do
print (g (undefined :: Int))
print (g (undefined :: Bool))
print (g (undefined :: Char))
data True
class CA t where
type A t; type A t = True
fa :: t -> String
instance CA Int where
fa _ = "Int"
class CB t where
type B t; type B t = True
fb :: t -> String
instance CB Bool where
fb _ = "Bool"
class CC t where
type C t; type C t = True
fc :: t -> String
instance CC Char where
fc _ = "Char"
class CAll t t1 t2 t3 where
g :: (t1 ~ A t, t2 ~ B t, t3 ~ C t) => t -> String
instance (CA t) => CAll t True t2 t3 where g = fa
instance (CB t) => CAll t t1 True t3 where g = fb
instance (CC t) => CAll t t1 t2 True where g = fc
When compiling without incoherent instances it claims multiple instances match. This would seem to imply that when incoherent instances are allowed, the instances would be chosen arbitrarily.
Unless I'm exceedingly lucky, this would result in compile errors as the instance constraints in most cases would not be satisfied.
But with incoherent instances I get no compile errors, and indeed get the following output with the "correct" instances chosen:
"Int"
"Bool"
"Char"
So I can only conclude either one of a few things here:
GHC is backtracking on instance context failures (something it says it doesn't do in it's own documentation)
GHC actually knows there's only one instance that matches, but isn't brave enough to use it unless one turns on incoherent instances
I've just got exceedingly lucky (1 in 33 = 27 chance)
Something else is happening.
I suspect the answer is 4 (maybe combined with 2). I'd like any answer to explain what's going on here, and how much I can rely on this behavior with incoherent instances? If it's reliable it seems I could use this behaviour to make quite complex class hierarchies, that actually behave somewhat like subtyping, e.g. I could say all types of class A and class B are in class C, and an instance writer could make an instance for A without having to explicitly make an instance for C.
Edit:
I suspect the answer has something to do with this in the GHC docs:
Find all instances I that match the target constraint; that is, the target constraint is a substitution instance of I. These instance declarations are the candidates.
Eliminate any candidate IX for which both of the following hold:
There is another candidate IY that is strictly more specific; that is, IY is a substitution instance of IX but not vice versa.
Either IX is overlappable, or IY is overlapping. (This "either/or" design, rather than a "both/and" design, allow a client to deliberately override an instance from a library, without requiring a change to the library.)
If exactly one non-incoherent candidate remains, select it. If all remaining candidates are incoherent, select an arbitary one. Otherwise the search fails (i.e. when more than one surviving candidate is not incoherent).
If the selected candidate (from the previous step) is incoherent, the search succeeds, returning that candidate.
If not, find all instances that unify with the target constraint, but do not match it. Such non-candidate instances might match when the target constraint is further instantiated. If all of them are incoherent, the search succeeds, returning the selected candidate; if not, the search fails.
Correct me if I'm wrong, but whether incoherent instances is selected or not, in the first step, there's only one instance that "matches". In the incoherent case, we fall out with that "matched" case at step 4. But in the non-incoherent case, we then go to step 4, and even though only one instance "matches", we find other instances "unify". So we must reject.
Is this understanding correct?
And if so, could someone explain exactly what "match" and "unify" mean, and the difference between them?

Haskell function returning existential type

Is it possible to write a Haskell function that yields a parameterised type where the exact type parameter is hidden? I.e. something like f :: T -> (exists a. U a)? The obvious attempt:
{-# LANGUAGE ExistentialQuantification #-}
data D a = D a
data Wrap = forall a. Wrap (D a)
unwrap :: Wrap -> D a
unwrap (Wrap d) = d
fails to compile with:
Couldn't match type `a1' with `a'
`a1' is a rigid type variable bound by
a pattern with constructor
Wrap :: forall a. D a -> Wrap,
in an equation for `unwrap'
at test.hs:8:9
`a' is a rigid type variable bound by
the type signature for unwrap :: Wrap -> D a at test.hs:7:11
Expected type: D a
Actual type: D a1
In the expression: d
In an equation for `unwrap': unwrap (Wrap d) = d
I know this is a contrived example, but I'm curious if there is a way to convince GHC that I do not care for the exact type with which D is parameterised, without introducing another existential wrapper type for the result of unwrap.
To clarify, I do want type safety, but also would like to be able to apply a function dToString :: D a -> String that does not care about a (e.g. because it just extracts a String field from D) to the result of unwrap. I realise there are other ways of achieving it (e.g. defining wrapToString (Wrap d) = dToString d) but I'm more interested in whether there is a fundamental reason why such hiding under existential is not permitted.
Yes, you can, but not in a straightforward way.
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE RankNTypes #-}
data D a = D a
data Wrap = forall a. Wrap (D a)
unwrap :: Wrap -> forall r. (forall a. D a -> r) -> r
unwrap (Wrap x) k = k x
test :: D a -> IO ()
test (D a) = putStrLn "Got a D something"
main = unwrap (Wrap (D 5)) test
You cannot return a D something_unknown from your function, but you can extract it and immediately pass it to another function that accepts D a, as shown.
Yes, you can convince GHC that you do not care for the exact type with which D is parameterised. Just, it's a horrible idea.
{-# LANGUAGE GADTs #-}
import Unsafe.Coerce
data D a = D a deriving (Show)
data Wrap where -- this GADT is equivalent to your `ExistentialQuantification` version
Wrap :: D a -> Wrap
unwrap :: Wrap -> D a
unwrap (Wrap (D a)) = D (unsafeCoerce a)
main = print (unwrap (Wrap $ D "bla") :: D Integer)
This is what happens when I execute that simple program:
and so on, until memory consumption brings down the system.
Types are important! If you circumvent the type system, you circumvent any predictability of your program (i.e. anything can happen, including thermonuclear war or the famous demons flying out of your nose).
Now, evidently you thought that types somehow work differently. In dynamic languages such as Python, and also to a degree in OO languages like Java, a type is in a sense a property that a value can have. So, (reference-) values don't just carry around the information needed to distinguish different values of a single type, but also information to distinguish different (sub-)types. That's in many senses rather inefficient – it's a major reason why Python is so slow and Java needs such a huge VM.
In Haskell, types don't exist at runtime. A function never knows what type the values have it's working with. Only because the compiler knows all about the types it will have, the function doesn't need any such knowledge – the compiler has already hard-coded it! (That is, unless you circumvent it with unsafeCoerce, which as I demonstrated is as unsafe as it sounds.)
If you do want to attach the type as a “property” to a value, you need to do it explicitly, and that's what those existential wrappers are there for. However, there are usually better ways to do it in a functional language. What's really the application you wanted this for?
Perhaps it's also helpful to recall what a signature with polymorphic result means. unwrap :: Wrap -> D a doesn't mean “the result is some D a... and the caller better don't care for the a used”. That would be the case in Java, but it would be rather useless in Haskell because there's nothing you can do with a value of unknown type.
Instead it means: for whatever type a the caller requests, this function is able to supply a suitable D a value. Of course this is tough to deliver – without extra information it's just as impossible as doing anything with a value of given unknown type. But if there are already a values in the arguments, or a is somehow constrained to a type class (e.g. fromInteger :: Num a => Integer -> a, then it's quite possible and very useful.
To obtain a String field – independent of the a parameter – you can just operate directly on the wrapped value:
data D a = D
{ dLabel :: String
, dValue :: a
}
data Wrap where Wrap :: D a -> Wrap
labelFromWrap :: Wrap -> String
labelFromWrap (Wrap (D l _)) = l
To write such functions on Wrap more generically (with any “ label accesor that doesn't care about a”), use Rank2-polymorphism as shown in n.m.'s answer.

Use of 'unsafeCoerce'

In Haskell, there is a function called unsafeCoerce, that turns anything into any other type of thing. What exactly is this used for? Like, why we would you want to transform things into each other in such an "unsafe" way?
Provide an example of a way that unsafeCoerce is actually used. A link to Hackage would help. Example code in someones question would not.
unsafeCoerce lets you convince the type system of whatever property you like. It's thus only "safe" exactly when you can be completely certain that the property you're declaring is true. So, for instance:
unsafeCoerce True :: Int
is a violation and can lead to wonky, bad runtime behavior.
unsafeCoerce (3 :: Int) :: Int
is (obviously) fine and will not lead to runtime misbehavior.
So what's a non-trivial use of unsafeCoerce? Let's say we've got an typeclass-bound existential type
module MyClass ( SomethingMyClass (..), intSomething ) where
class MyClass x where {}
instance MyClass Int where {}
data SomethingMyClass = forall a. MyClass a => SomethingMyClass a
Let's also say, as noted here, that the typeclass MyClass is not exported and thus nobody else can ever create instances of it. Indeed, Int is the only thing that instantiates it and the only thing that ever will.
Now when we pattern match to destruct a value of SomethingMyClass we'll be able to pull a "something" out from inside
foo :: SomethingMyClass -> ...
foo (SomethingMyClass a) =
-- here we have a value `a` with type `exists a . MyClass a => a`
--
-- this is totally useless since `MyClass` doesn't even have any
-- methods for us to use!
...
Now, at this point, as the comment suggests, the value we've pulled out has no type information—it's been "forgotten" by the existential context. It could be absolutely anything which instantiates MyClass.
Of course, in this very particular situation we know that the only thing implementing MyClass is Int. So our value a must actually have type Int. We could never convince the typechecker that this is true, but due to an outside proof we know that it is.
Therefore, we can (very carefully)
intSomething :: SomethingMyClass -> Int
intSomething (SomethingMyClass a) = unsafeCoerce a -- shudder!
Now, hopefully I've suggested that this is a terrible, dangerous idea, but it also may give a taste of what kind of information we can take advantage of in order to know things that the typechecker cannot.
In non-pathological situations, this is rare. Even rarer is a situation where using something we know and the typechecker doesn't isn't itself pathological. In the above example, we must be completely certain that nobody ever extends our MyClass module to instantiate more types to MyClass otherwise our use of unsafeCoerce becomes instantly unsafe.
> instance MyClass Bool where {}
> intSomething (SomethingMyClass True)
6917529027658597398
Looks like our compiler internals are leaking!
A more common example where this sort of behavior might be valuable is when using newtype wrappers. It's a fairly common idea that we might wrap a type in a newtype wrapper in order to specialize its instance definitions.
For example, Int does not have a Monoid definition because there are two natural monoids over Ints: sums and products. Instead, we use newtype wrappers to be more explicit.
newtype Sum a = Sum { getSum :: a }
instance Num a => Monoid (Sum a) where
mempty = Sum 0
mappend (Sum a) (Sum b) = Sum (a+b)
Now, normally the compiler is pretty smart and recognizes that it can eliminate all of those Sum constructors in order to produce more efficient code. Sadly, there are times when it cannot, especially in highly polymorphic situations.
If you (a) know that some type a is actually just a newtype-wrapped b and (b) know that the compiler is incapable of deducing this itself, then you might want to do
unsafeCoerce (x :: a) :: b
for a slight efficiency gain. This, for instance, occurs frequently in lens and is expressed in the Data.Profunctor.Unsafe module of profunctors, a dependency of lens.
But let me again suggest that you really need to know what's going on before using unsafeCoerce like this is anything but highly unsafe.
One final thing to compare is the "typesafe cast" available in Data.Typeable. This function looks a bit like unsafeCoerce, but with much more ceremony.
unsafeCoerce :: a -> b
cast :: (Typeable a, Typeable b) => a -> Maybe b
Which, you might think of as being implemented using unsafeCoerce and a function typeOf :: Typeable a => a -> TypeRep where TypeRep are unforgeable, runtime tokens which reflect the type of a value. Then we have
cast :: (Typeable a, Typeable b) => a -> Maybe b
cast a = if (typeOf a == typeOf b) then Just b else Nothing
where b = unsafeCoerce a
Thus, cast is able to ensure that the types of a and b really are the same at runtime, and it can decide to return Nothing if they are not. As an example:
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE ExistentialQuantification #-}
data A = A deriving (Show, Typeable)
data B = B deriving (Show, Typeable)
data Forget = forall a . Typeable a => Forget a
getAnA :: Forget -> Maybe A
getAnA (Forget something) = cast something
which we can run as follows
> getAnA (Forget A)
Just A
> getAnA (Forget B)
Nothing
So if we compare this usage of cast with unsafeCoerce we see that it can achieve some of the same functionality. In particular, it allows us to rediscover information that may have been forgotten by ExistentialQuantification. However, cast manually checks the types at runtime to ensure that they are truly the same and thus cannot be used unsafely. To do this, it demands that both the source and target types allow for runtime reflection of their types via the Typeable class.
The only time I ever felt compelled to use unsafeCoerce was on finite natural numbers.
{-# LANGUAGE DataKinds, GADTs, TypeFamilies, StandaloneDeriving #-}
data Nat = Z | S Nat deriving (Eq, Show)
data Fin (n :: Nat) :: * where
FZ :: Fin (S n)
FS :: Fin n -> Fin (S n)
deriving instance Show (Fin n)
Fin n is a singly linked data structure that is statically ensured to be smaller than the n type level natural number by which it is parametrized.
-- OK, 1 < 2
validFin :: Fin (S (S Z))
validFin = FS FZ
-- type error, 2 < 2 is false
invalidFin :: Fin (S (S Z))
invalidFin = FS (FS FZ)
Fin can be used to safely index into various data structures. It's pretty standard in dependently typed languages, though not in Haskell.
Sometimes we want to convert a value of Fin n to Fin m where m is greater than n.
relaxFin :: Fin n -> Fin (S n)
relaxFin FZ = FZ
relaxFin (FS n) = FS (relaxFin n)
relaxFin is a no-op by definition, but traversing the value is still required for the types to check out. So we might just use unsafeCoerce instead of relaxFin. More pronounced gains in speed can result from coercing larger data structures that contain Fin-s (for example, you could have lambda terms with Fin-s as bound variables).
This is an admittedly exotic example, but I find it interesting in the sense that it's pretty safe: I can't really think of ways for external libraries or safe user code to mess this up. I might be wrong though and I'd be eager to hear about potential safety issues.
There is no use of unsafeCoerce I can really recommend, but I can see that in some cases such a thing might be useful.
The first use that springs to mind is the implementation of the Typeable-related routines. In particular cast :: (Typeable a, Typeable b) => a -> Maybe b achieves a type-safe behaviour, so it is safe to use, yet it has to play dirty tricks in its implementation.
Maybe unsafeCoerce can find some use when importing FFI subroutines to force types to match. After all, FFI already allows to import impure C functions as pure ones, so it is intrinsecally usafe. Note that "unsafe" does not mean impossible to use, but just "putting the burden of proof on the programmer".
Finally, pretend that sortBy did not exist. Consider then this example:
-- Like Int, but using the opposite ordering
newtype Rev = Rev { unRev :: Int }
instance Ord Rev where compare (Rev x) (Rev y) = compare y x
sortDescending :: [Int] -> [Int]
sortDescending = map unRev . sort . map Rev
The code above works, but feels silly IMHO. We perform two maps using functions such as Rev,unRev which we know to be no-ops at runtime. So we just scan the list twice for no reason, but that of convincing the compiler to use the right Ord instance.
The performance impact of these maps should be small since we also sort the list. Yet it is tempting to rewrite map Rev as unsafeCoerce :: [Int]->[Rev] and save some time.
Note that having a coercing function
castNewtype :: IsNewtype t1 t2 => f t2 -> f t1
where the constraint means that t1 is a newtype for t2 would help, but it would be quite dangerous. Consider
castNewtype :: Data.Set Int -> Data.Set Rev
The above would cause the data structure invariant to break, since we are changing the ordering underneath! Since Data.Set is implemented as a binary search tree, it would cause quite a large damage.

Associated Parameter Restriction using Functional Dependency

The function f below, for a given type 'a', takes a parameter of type 'c'. For different types 'a', 'c' is restricted in different ways. Concretely, when 'a' is any Integral type, 'c' should be allowed to be any 'Real' type. When 'a' is Float, 'c' can ONLY be Float.
One attempt is:
{-# LANGUAGE
MultiParamTypeClasses,
FlexibleInstances,
FunctionalDependencies,
UndecidableInstances #-}
class AllowedParamType a c | a -> c
class Foo a where
f :: (AllowedParamType a c) => c -> a
fIntegral :: (Integral a, Real c) => c -> a
fIntegral = error "implementation elided"
instance (Integral i, AllowedParamType i d, Real d) => Foo i where
f = fIntegral
For some reason, GHC 7.4.1 complains that it "could not deduce (Real c) arising from a use of fIntegral". It seems to me that the functional dependency should allow this deduction. In the instance, a is unified with i, so by the functional dependency, d should be unified with c, which in the instance is declared to be 'Real'. What am I missing here?
Functional dependencies aside, will this approach be expressive enough to enforce the restrictions above, or is there a better way? We are only working with a few different values for 'a', so there will be instances like:
instance (Integral i, Real c) => AllowedParamType i c
instance AllowedParamType Float Float
Thanks
A possibly better way, is to use constraint kinds and type families (GHC extensions, requires GHC 7.4, I think). This allows you to specify the constraint as part of the class instance.
{-# LANGUAGE ConstraintKinds, TypeFamilies, FlexibleInstances, UndecidableInstances #-}
import GHC.Exts (Constraint)
class Foo a where
type ParamConstraint a b :: Constraint
f :: ParamConstraint a b => b -> a
instance Integral i => Foo i where
type ParamConstraint i b = Real b
f = fIntegral
EDIT: Upon further experimentation, there are some subtleties that mean that this doesn't work as expected, specifically, type ParamConstraint i b = Real b is too general. I don't know a solution (or if one exists) right now.
OK, this one's been nagging at me. given the wide variety of instances,
let's go the whole hog and get rid of any relationship between the
source and target type other than the presence of an instance:
{-# LANGUAGE OverlappingInstances, FlexibleInstances,TypeSynonymInstances,MultiParamTypeClasses #-}
class Foo a b where f :: a -> b
Now we can match up pairs of types with an f between them however we like, for example:
instance Foo Int Int where f = (+1)
instance Foo Int Integer where f = toInteger.((7::Int) -)
instance Foo Integer Int where f = fromInteger.(^ (2::Integer))
instance Foo Integer Integer where f = (*100)
instance Foo Char Char where f = id
instance Foo Char String where f = (:[]) -- requires TypeSynonymInstances
instance (Foo a b,Functor f) => Foo (f a) (f b) where f = fmap f -- requires FlexibleInstances
instance Foo Float Int where f = round
instance Foo Integer Char where f n = head $ show n
This does mean a lot of explicit type annotation to avoid No instance for... and Ambiguous type error messages.
For example, you can't do main = print (f 6), but you can do main = print (f (6::Int)::Int)
You could list all of the instances with the standard types that you want,
which could lead to an awful lot of repetition, our you could light the blue touchpaper and do:
instance Integral i => Foo Double i where f = round -- requires FlexibleInstances
instance Real r => Foo Integer r where f = fromInteger -- requires FlexibleInstances
Beware: this does not mean "Hey, if you've got an integral type i,
you can have an instance Foo Double i for free using this handy round function",
it means: "every time you have any type i, it's definitely an instance
Foo Double i. By the way, I'm using round for this, so unless your type i is Integral,
we're going to fall out." That's a big issue for the Foo Integer Char instance, for example.
This can easily break your other instances, so if you now type f (5::Integer) :: Integer you get
Overlapping instances for Foo Integer Integer
arising from a use of `f'
Matching instances:
instance Foo Integer Integer
instance Real r => Foo Integer r
You can change your pragmas to include OverlappingInstances:
{-# LANGUAGE OverlappingInstances, FlexibleInstances,TypeSynonymInstances,MultiParamTypeClasses #-}
So now f (5::Integer) :: Integer returns 500, so clearly it's using the more specific Foo Integer Integer instance.
I think this sort of approach might work for you, defining many instances by hand, carefully considering when to go completely wild
making instances out of standard type classes. (Alternatively, there aren't all that many standard types, and as we all know, notMany choose 2 = notIntractablyMany, so you could just list them all.)
Here's a suggestion to solve a more general problem, not yours specifically (I need more detail yet first - I promise to check later). I'm writing it in case other people are searching for a solution to a similar problem to you, I certainly was in the past, before I discovered SO. SO is especially great when it helps you try a radically new approach.
I used to have the work habit:
Introduce a multi-parameter type class (Types hanging out all over the place, so...)
Introduce functional dependencies (Should tidy it up but then I end up needing...)
Add FlexibleInstances (Alarm bells start ringing. There's a reason the compiler has this off by default...)
Add UndecidableInstances (GHC is telling you you're on your own, because it's not convinced it's up to the challenge you're setting it.)
Everything blows up. Refactor somehow.
Then I discovered the joys of type families (functional programming for types (hooray) - multi-parameter type classes are (a bit like) logic programming for types). My workflow changed to:
Introduce a type class including an associated type, i.e. replace
class MyProblematicClass a b | a -> b where
thing :: a -> b
thang :: b -> a -> b
with
class MyJustWorksClass a where
type Thing a :: * -- Thing a is a type (*), not a type constructor (* -> *)
thing :: a -> Thing a
thang :: Thing a -> a -> Thing a
Nervously add FlexibleInstances. Nothing goes wrong at all.
Sometimes fix things by using constraints like (MyJustWorksClass j,j~a)=> instead of (MyJustWorksClass a)=> or (Show t,t ~ Thing a,...)=> instead of (Show (Thing a),...) => to help ghc out. (~ essentially means 'is the same type as')
Nervously add FlexibleContexts. Nothing goes wrong at all.
Everything works.
The reason "Nothing goes wrong at all" is that ghc calculates the type Thing a using my type function Thang rather than trying to deduce it using a merely a bunch of assertions that there's a function there and it ought to be able to work it out.
Give it a go! Read Fun with Type Functions before reading the manual!

Resources