To avoid this, you can wrap type in newtype
newtype FirstMonad a = FirstMonad (ReaderT (IORef String) IO a)
But after deep considering you feel need orphan instances, you can suppress warnings:
{-# OPTIONS_GHC -fno-warn-orphans #-}
Detail
Coherence
For example, considering following definition for now:
data A = A
instance Eq A where
...
It can be regarded as type based overloading. In the above, Checking equality (==)
is able to be used under various types:
f :: Eq a => a -> a -> a -> Bool
f x y z = x == y && y == z
g :: A -> A -> A -> Bool
g x y z = x == y && y == z
In definition of f
, type a
is abstract and under constraint Eq
, but in g
, type A
is concrete. The former derives method from constrains, but Haskell also in the latter can derive. How to derive is to just elaborate Haskell into language which has no type class. This way is called dictionary passing.
class C a where
m1 :: a -> a
instance C A where
m1 x = x
f :: C a => a -> a
f = m1 . m1
It will be converted:
data DictC a = DictC
{ m1 :: a -> a
}
instDictC_A :: DictC A
instDictC_A = DictC
{ m1 = \x -> x
}
f :: DictC a -> a -> a
f d = m1 d . m1 d
As the above, make a data type called dictionary corresponds to a type class, and pass the value of the type.
Haskell has a constraint that a type may not be declared as an instance of a particular class more than once in the program. This causes various problems.
class C1 a where
m1 :: a
class C1 a => C2 a where
m2 :: a -> a
instance C1 Int where
m1 = 0
instance C2 Int where
m2 x = x + 1
f :: (C1 a, C2 a) => a
f = m2 m1
g :: Int
g = f
This code uses inheritance of type class. It derives following elaborated code.
{ m1 :: a
}
data DictC2 a = DictC2
{ superC1 :: DictC1 a
, m2 :: a -> a
}
instDictC1_Int :: DictC1 Int
instDictC1_Int = DictC1
{ m1 = 0
}
instDictC2_Int :: DictC2 Int
instDictC2_Int = DictC2
{ superC1 = instDictC1_Int
, m2 = \x -> x + 1
}
f :: DictC1 a -> DictC2 a -> a
f d1 d2 = ???
g :: Int
g = f instDictC1_Int instDictC2_Int
Well, what is definition of f
going on? Actually, Definition's' are following:
f :: DictC1 a -> DictC2 a -> a
f d1 d2 = m2 d2 (m1 d1)
f :: DictC1 a -> DictC2 a -> a
f _ d2 = m2 d2 (m1 d1)
where
d1 = superC1 d2
Do you confirm it has no problem in typing? If Haskell can define Int
as a instance of C1
repeatedly, superC1
in DictC2
will be filled in elaboration, the value will be probably defferent from DictC1 a
passed to f
when call g
.
Let's see more example:
h :: (Int, Int)
h = (m1, m1)
Of course, elaboration is one:
h :: (Int, Int)
h = (m1 instDictC1_Int, m1 instDictC1_Int)
But if can define instance repeatedly, can also consider following elaboration:
h :: (Int, Int)
h = (m1 instDictC1_Int, m1 instDictC1_Int')
Hence, two same types are applied two different instances. For example, calling same function twice, but returns different value by different algorithm possibly.
The stated example is little bit exaggerated, though how about next example?
instance C1 Int where
m1 = 0
h1 :: Int
h1 = m1
instance C1 Int where
m1 = 1
h2 :: (Int, Int)
h2 = (m1, h1)
In this case, quite possibly use different instances m1
in h1
and m1
in h2
.
Haskell often prefers to transformation based on equational reasoning, so it will be a problem that h1
is not able to be replaced directly to m1
.
Generally, type system include resolving instances of type classes. In such a case, resolve instances when check types. And codes are elaborated by derivation tree made during checking types. Such transformation is sometimes adapted by besides type class, specifically, implicit type conversion, record type and so on. Then, these cases possibly cause the problem as the above. This problem can formalized following:
When convert derivation tree of type into language, in two different derivation tree of one type, results of conversion don't become semantically equivalent.
As the stated, even apply whatever instance matches type, and it generally must pass type checking. However, a result of elaboration by using a instance is possibly different a result of elaboration after resolving other instance. Vice versa, if don't have this problem, can acquire certain guarantee of type system. This guarantee, a combination of type system which the problem formalized above doesn't work and property pf elaboration, is generally called coherence. There are some way to guarantee coherence, Haskell limits number of instance definition corresponding type class to one in order to guarantee coherence.
Orphan Instance
How Haskell does is easy to say, but has some issues. Quite famous one is orphan instance. GHC, in a type declaration T
as an instance of C
, treatment of instance depends on whether or not the declaration is in a same module which has declaration T
or C
. Especially, not in same module, called orphan instance, GHC will warn. Why how it works?
First, in Haskell, instances propagate implicitly between modules. This is stipulated as following:
All instances in scope within a module are always exported and any import brings all instances in from the imported module. Thus, an instance declaration is in scope if and only if a chain of import declarations leads to the module containing the instance declaration.
--5 Modules
We can't stop this, can't control this. In the first place, Haskell decided to let us define one type as one instance, so it's unnecessary to mind it. By the way, it's as good there is such regulation, actually compiler of Haskell must resolve instances according to the regulation. Of course, compiler doesn't know which modules have instances, must check all modules at worst case. It also bothers us. If two important modules hold each instance definition toward same type, all modules which have their import chains include the modules become unavailable in order to conflict.
Well, to use a type as a instance of a class, we need information of them, so we will go to see a module which has declarations. Then, that a third party fiddles the module is not going to happen. Therefore, if either of the modules includes the instance declaration, compiler can see necessary information with instances, we are happy that enable to load modules guarantees that they have no conflicts. For that reason, that a type as an instance of a class placed in a same module which has declaration the type or the class is being recommended. On the contrary, avoiding orphan instance as much as possible is being recommended. Hence, if want to make a type as a independent instance, making a new type by newtype
in order to only change semantics of a instance, declaring the type as the instance.
In addition, GHC marks up internally modules have orphan instances, modules have orphan instances are enumerated in their dependent modules' interface files. And then, compiler refers all of the list. Thus, to make orphan instance once, an interface file of a module which has the instance, when all modules depend on the module recompile, will reloaded if whatever changes. So, orphan instance affects bad to compile time.
Detail is under CC BY-SA 4.0 (C) Mizunashi Mana
Original is 続くといいな日記 – 型クラスの Coherence と Orphan Instance
2020-12-22 revised and translated by Akihito Kirisaki