Orphan instance
An instance is an orphan when the module declaring it owns neither the class nor the type[1]. In Haskell, type class instances are global: once a module defining an instance is anywhere in a program, the instance is visible to every module that imports it, directly or indirectly, and there is no way to hide it[2]. Code that relies on classes such as Ord assumes global uniqueness, that each class has at most one instance for each type in the whole program[3]. An instance defined with its class or its type cannot be duplicated without a compile error, but two orphans for the same pair, in two unrelated libraries, can both compile, and a program that links both may use one in one place and the other in another. GHC warns about orphans with -Worphans; Rust's coherence rules forbid them[4].
Example
The fix is the newtype, which makes the choice local and explicit rather than global and ambient.
-- orphan: neither Ord nor Text is oursinstance Ord Text where compare = compareCaseInsensitively-- not an orphan: we own CI, so the instance travels with itnewtype CI = CI Textinstance Ord CI wherecompare (CI a) (CI b) = compareCaseInsensitively a b
The first instance can be defined by any library, and any other library can define a different one. The second can only be defined in the module that declares CI, or in the module that declares Ord, so there is exactly one.
Why orphans break things
The problem is a mismatch in scoping: instance resolution is global and implicit, but imports are local and explicit. GHC checks for duplicate instances lazily, only when a module actually needs to resolve a constraint for which two instances are visible[1][5]. Two modules can therefore each define an orphan Ord instance for the same type, each compile against its own, and export functions whose behavior depends on it. A program that imports both compiles as long as it never asks for the instance itself.
A type with no Ord instance.
module Types (Version (..)) where-- A type from one library, with no Ord instance of its own.data Version = Version Int Int deriving (Eq, Show)
One library orders versions by (major, minor) and builds a Set.
module Catalog (catalog) whereimport qualified Data.Set as Setimport Types-- Orphan: this module owns neither Ord nor Version.instance Ord Version wherecompare (Version a b) (Version c d) = compare (a, b) (c, d)catalog :: Set.Set Versioncatalog = Set.fromList [Version m n | m <- [1 .. 3], n <- [0 .. 3]]
Another orders them by (minor, major) and searches a Set.
module Query (available) whereimport qualified Data.Set as Setimport Types-- A second orphan for the same pair, ordering by minor version first.instance Ord Version wherecompare (Version a b) (Version c d) = compare (b, a) (d, c)available :: Version -> Set.Set Version -> Boolavailable = Set.member
A program using both.
import qualified Data.Set as Setimport Catalog (catalog)import Query (available)import Types-- Main sees both instances but never resolves Ord Version itself, so GHC-- does not report the conflict.main :: IO ()main = doprint (Set.size catalog)print [ (m, n) | m <- [1 .. 3], n <- [0 .. 3], not (available (Version m n) catalog) ]
Running it: the set has 12 elements, and half of them cannot be found.
12[(2,0),(2,1),(2,2),(3,0),(3,1),(3,2)]
Data.Set is a balanced search tree. Its invariant, that the elements are sorted by the Ord instance, was established under one instance and is being relied on under another, so a lookup follows the wrong branches. No exception is raised and nothing in the types reveals it. The same happens with Map keys, Hashable instances and hash maps, and any structure that stores an invariant computed from a class method.
If the program resolves the instance itself, GHC does report the conflict.
The same lookup, written in a module that sees both instances.
import qualified Data.Set as Setimport Catalog (catalog)import Query (available)import Typesmain :: IO ()main = print (Set.member (Version 2 0) catalog)
GHC's error.
Direct.hs:7:15: error:* Overlapping instances for Ord Versionarising from a use of `Set.member'Matching instances:instance Ord Version -- Defined at Query.hs:7:10instance Ord Version -- Defined at Catalog.hs:7:10* In the first argument of `print', namely`(Set.member (Version 2 0) catalog)'In the expression: print (Set.member (Version 2 0) catalog)In an equation for `main':main = print (Set.member (Version 2 0) catalog)|7 | main = print (Set.member (Version 2 0) catalog)| ^^^^^^^^^^
Coherence and global uniqueness
Two properties are involved[3][5]. Coherence says that every valid typing of a program has the same meaning, so the choice of instance at a given use does not depend on how the type checker happened to derive it. Global uniqueness says that there is at most one instance of a class for a type in the entire program. Haskell's design guarantees coherence only given global uniqueness, and libraries such as containers depend on global uniqueness for correctness: a Set a does not carry its ordering, but assumes that the Ord a at every later use is the one it was built with. Orphans are the way global uniqueness fails without the compiler noticing.
Warnings and costs
The warning, reported where the orphan is defined.
ghc -Worphans Catalog.hs
GHC's output.
Catalog.hs:7:1: warning: [-Worphans]Orphan instance: instance Ord VersionSuggested fix:Move the instance declaration to the module of the class or of the type, orwrap the type with a newtype and declare the instance on the new type.|7 | instance Ord Version where| ^^^^^^^^^^^^^^^^^^^^^^^^^^...
Orphans also cost compile time. For an ordinary instance, GHC can find it by loading the interface of the module that defines the class or the type, which it needs anyway. An orphan could be anywhere, so GHC records every orphan module in the interface files of the modules that depend on it and loads all of them whenever it compiles a module that might need one[1].
Avoiding orphans
The standard fix is a newtype. Wrapping the type gives the instance a type of its own, defined in the same module, so the instance is not an orphan and cannot be duplicated. The two orderings become two types, and the compiler stops a set built under one from being searched under the other.
Two orderings as two newtypes.
import qualified Data.Set as Setdata Version = Version Int Int deriving (Eq, Show)-- Each ordering is a newtype owned by the code that wants it, so the-- instance travels with the type and there is only ever one per type.newtype ByRelease = ByRelease Version deriving (Eq, Show)newtype ByMinor = ByMinor Version deriving (Eq, Show)instance Ord ByRelease wherecompare (ByRelease (Version a b)) (ByRelease (Version c d)) = compare (a, b) (c, d)instance Ord ByMinor wherecompare (ByMinor (Version a b)) (ByMinor (Version c d)) = compare (b, a) (d, c)main :: IO ()main = dolet versions = [Version m n | m <- [1 .. 3], n <- [0 .. 3]]releases = Set.fromList (map ByRelease versions)-- Set ByRelease and Set ByMinor are different types, so a set built under-- one ordering cannot be searched under the other.print (all (\v -> Set.member (ByRelease v) releases) versions)print (take 3 (Set.toList (Set.fromList (map ByMinor versions))))
Running it.
True[ByMinor (Version 1 0),ByMinor (Version 2 0),ByMinor (Version 3 0)]
Other options are to contribute the instance upstream, to the package that defines the class or the type, or to put a widely needed orphan in a single package that everyone agrees to use; packages such as quickcheck-instances collect instances of one library's classes for other libraries' types in this way. GHC's DerivingVia reduces the boilerplate of newtypes by deriving the instance from another type's representation.
In other languages
Rust enforces coherence statically with its orphan rule: an impl of a trait for a type is allowed only in the crate that defines the trait or the type, with some relaxations for local types appearing as type parameters[4]. This keeps global uniqueness but means that a program cannot implement a trait from one library for a type from another at all; the newtype pattern is the standard workaround. Scala's implicits and given instances are lexically scoped rather than global, so there is no global uniqueness to break, but a sorted collection must then store its ordering, or risk the same mismatch[6]. ML functors pass the comparison explicitly, as in OCaml's Set.Make, whose result type records which ordering was used[7].
History
Wadler and Blott's type classes of 1989 assumed a single instance per class and type[2]. The Haskell reports did not restrict where instances may appear, and the problems of overlapping and duplicated instances were among the design questions surveyed by Peyton Jones, Jones and Meijer in 1997[5]. GHC introduced the orphan warning and its interface-file bookkeeping for orphan modules as the Haskell package ecosystem grew[1]. Rust adopted its orphan rule with the language's trait coherence design, and RFC 1023 in 2015 gave it its present form[4].
see also
- Overlapping instancesTwo type class instances overlap when some type matches both of their heads, as Pretty [a] and Pretty String do at [Char]. Instance resolution in Haskell does not backtrack, so by default GHC reports an error rather than choose. The OVERLAPPING and OVERLAPPABLE pragmas let the most specific matching instance win, which is predictable when the choice is made at a known type and all the instances are in scope, but can make the same expression behave differently in different modules. INCOHERENT lets GHC choose even when the choice depends on a type it does not yet know. Closed type families and newtypes are the usual alternatives.
- Deriving strategyA deriving strategy is an explicit choice of how GHC produces a derived type class instance: stock, generated by the compiler from the constructors; newtype, reusing the wrapped type's instance by coercion; anyclass, an empty instance that takes the class's default methods; or via, reusing the instance of another type with the same representation. Several strategies can apply to the same declaration, with different results, so naming the strategy removes the ambiguity. The two coercing strategies are sound only when the class's methods can be coerced, which GHC checks with type roles.
- Dictionary passingDictionary passing is the standard way to implement type classes. Each class becomes a record type whose fields are its methods, each instance becomes a value of that record type, and each function with a class constraint receives the record, the dictionary, as an extra argument, which the compiler supplies at every call site after resolving which instance applies. An instance that itself has a context, such as equality on lists given equality on elements, becomes a function from dictionaries to a dictionary, and a superclass becomes a field. The same technique written by hand is the usual way to get type-class-like overloading in OCaml, with records, first-class modules or functors.
- Higher-kinded typeA higher-kinded type is a type constructor, such as Maybe or List, that takes types as arguments, and higher-kinded polymorphism is the ability to abstract over such constructors with type variables, so that Functor f can be instantiated at Maybe or at lists without naming their element types. Kinds classify types the way types classify values: Int has kind Type, Maybe has kind Type → Type. Higher-kinded variables are what make a single Functor or Monad class usable across every container. Haskell and Scala support them directly; OCaml expresses the same abstraction with module functors or an encoding, and Java and Go cannot express it.
further reading
- [1]GHC User’s Guide, “Orphan modules and instance declarations” and the -Worphans warning.
- [2]P. Wadler, S. Blott, “How to make ad-hoc polymorphism less ad hoc”, POPL (1989).
- [3]E. Z. Yang, “Type classes: confluence, coherence and global uniqueness”, blog post (2014).
- [4]Rust RFC 1023, “Rebalancing coherence” (2015).
- [5]S. Peyton Jones, M. Jones, E. Meijer, “Type classes: an exploration of the design space”, Haskell Workshop (1997).
- [6]B. C. d. S. Oliveira, A. Moors, M. Odersky, “Type classes as objects and implicits”, OOPSLA (2010).
- [7]D. Dreyer, R. Harper, M. M. T. Chakravarty, G. Keller, “Modular type classes”, POPL (2007).
last updated