Chapter 5
Classes, instances and deriving
Where a class declaration becomes a record of functions, an instance becomes a value of that record, and `deriving` writes code you never see: the machinery that makes overloading disappear before Core.
Where this lives in the tree
-
GHC/Tc/TyCl.hschecking type, class and family declarations -
GHC/Tc/TyCl/Instance.hsinstance declarations and superclass evidence -
GHC/Tc/Instance/Class.hsmatching a wanted dictionary against instances -
GHC/Tc/Deriv.hsthe deriving mechanisms and strategy selection
Type classes are the feature that most distinguishes Haskell, and the one whose implementation is least visible from the outside. By the time your program reaches Core there are no classes at all: only records, ordinary functions, and ordinary arguments.
This chapter is how that erasure happens.
Dictionaries
A class becomes a data type; its methods become fields. Show a is, after
elaboration, a record holding showsPrec, show and showList. An instance
becomes a value of that record. A constraint Show a => becomes a function
argument carrying it.
You saw the result in the parser chapter without comment:
describe :: forall a. Show a => a -> String
describe
= \ (@a) ($dShow :: Show a) (x :: a) ->
++ (unpackCString# "value: "#) (show $dShow x)
$dShow is the dictionary. show $dShow x is a field selection followed by an
application. Nothing about that is class-specific any more, which is precisely
the point, because it means the whole optimiser downstream needs to know nothing
about classes.
Single-method classes get a further simplification: rather than a one-field record, the dictionary is the method, via a newtype. It is why a single-method class often costs nothing at runtime.
Solving a dictionary constraint
When the solver meets a Wanted Show Int, it consults the instance environment
for a match. Success produces evidence (the dictionary) and fills the hole.
Superclasses complicate this pleasantly. If you have Ord a, you also have
Eq a, because Ord declares Eq as a superclass, and the Ord dictionary
physically contains the Eq one. Extracting it is a field selection:
How do we ensure that every superclass witness in an instance declaration is generated by one of (sc1) (sc2) or (sc3) in Note [Recursive superclasses]? Answer: * The "given" constraints of an instance decl have CtOrigin of (GivenOrigin (InstSkol head_size)), where head_size is the PatersonSize of the head of the instance declaration. E.g. in instance D a => C [a] the `[G] D a` constraint has a CtOrigin whose head_size is the PatersonSize of (C [a]). * When we make a superclass selection from a Given (transitively) we give it a CtOrigin of (GivenSCOrigin skol_info sc_depth blocked). The 'blocked :: Bool' flag says if the superclass can be used to solve a superclass Wanted. The new superclass is blocked unless: it is the superclass of an unblocked dictionary (wrinkle (W1)), that is Paterson-smaller than the instance head. This is implemented in GHC.Tc.Solver.Dict.mk_strict_superclasses (in the mk_given_loc helper function). * Superclass "Wanted" constraints have CtOrigin of (ScOrigin NakedSc) The 'NakedSc' says that this is a naked superclass Wanted; we must be careful when solving it.
Show the rest of this Note (64 more lines)
* (sc1) When we rewrite such a wanted constraint, it retains its
origin. But if we apply an instance declaration, we can set the
origin to (ScOrigin NotNakedSc), thus lifting any restrictions by
making prohibitedSuperClassSolve return False. This happens
in GHC.Tc.Solver.Dict.checkInstanceOK.
* (sc2) ScOrigin wanted constraints can't be solved from a
superclass selection, except at a smaller type. This test is
implemented by GHC.Tc.Solver.InertSet.prohibitedSuperClassSolve
Note [Silent superclass arguments] (historical interest only)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NB1: this note describes our *old* solution to the
recursive-superclass problem. I'm keeping the Note
for now, just as institutional memory.
However, the code for silent superclass arguments
was removed in late Dec 2014
NB2: the silent-superclass solution introduced new problems
of its own, in the form of instance overlap. Tests
SilentParametersOverlapping, T5051, and T7862 are examples
NB3: the silent-superclass solution also generated tons of
extra dictionaries. For example, in monad-transformer
code, when constructing a Monad dictionary you had to pass
an Applicative dictionary; and to construct that you need
a Functor dictionary. Yet these extra dictionaries were
often never used. Test T3064 compiled *far* faster after
silent superclasses were eliminated.
Our solution to this problem "silent superclass arguments". We pass
to each dfun some ``silent superclass arguments’’, which are the
immediate superclasses of the dictionary we are trying to
construct. In our example:
dfun :: forall a. C [a] -> D [a] -> D [a]
dfun = \(dc::C [a]) (dd::D [a]) -> DOrd dc ...
Notice the extra (dc :: C [a]) argument compared to the previous version.
This gives us:
DFun Superclass Invariant
~~~~~~~~~~~~~~~~~~~~~~~~
In the body of a DFun, every superclass argument to the
returned dictionary is
either * one of the arguments of the DFun,
or * constant, bound at top level
This net effect is that it is safe to treat a dfun application as
wrapping a dictionary constructor around its arguments (in particular,
a dfun never picks superclasses from the arguments under the
dictionary constructor). No superclass is hidden inside a dfun
application.
The extra arguments required to satisfy the DFun Superclass Invariant
always come first, and are called the "silent" arguments. You can
find out how many silent arguments there are using Id.dfunNSilent;
and then you can just drop that number of arguments to see the ones
that were in the original instance declaration.
DFun types are built (only) by MkId.mkDictFunId, so that is where we
decide what silent arguments are to be added. The uncomfortable case is when a local Given and a global instance both match.
The chapter on the solver quoted Instance and Given overlap for this reason:
picking the instance would be wrong, because the caller may be passing a
different dictionary for the same type.
Deriving
deriving is code generation. GHC writes out an instance declaration and
typechecks it as though you had written it, which is why derived instances can
fail with ordinary type errors.
There is more than one way to derive, and choosing between them is explicit:
GHC has a notion of deriving strategies, which allow the user to explicitly request which approach to use when deriving an instance (enabled with the -XDerivingStrategies language extension). For more information, refer to the original issue (#10598) or the associated wiki page: https://gitlab.haskell.org/ghc/ghc/wikis/commentary/compiler/deriving-strategies A deriving strategy can be specified in a deriving clause: newtype Foo = MkFoo Bar deriving newtype C Or in a standalone deriving declaration: deriving anyclass instance C Foo -XDerivingStrategies also allows the use of multiple deriving clauses per data declaration so that a user can derive some instance with one deriving strategy and other instances with another deriving strategy. For example: newtype Baz = Baz Quux deriving (Eq, Ord) deriving stock (Read, Show) deriving newtype (Num, Floating) deriving anyclass C Currently, the deriving strategies are: * stock: Have GHC implement a "standard" instance for a data type, if possible (e.g., Eq, Ord, Generic, Data, Functor, etc.)
Show the rest of this Note (26 more lines)
* anyclass: Use -XDeriveAnyClass
* newtype: Use -XGeneralizedNewtypeDeriving
* via: Use -XDerivingVia
The latter two strategies (newtype and via) are referred to as the
"coerce-based" strategies, since they generate code that relies on the `coerce`
function. See, for instance, GHC.Tc.Deriv.Infer.inferConstraintsCoerceBased.
The former two strategies (stock and anyclass), in contrast, are
referred to as the "originative" strategies, since they create "original"
instances instead of "reusing" old instances (by way of `coerce`).
See, for instance, GHC.Tc.Deriv.Utils.checkOriginativeSideConditions.
If an explicit deriving strategy is not given, GHC has an algorithm it uses to
determine which strategy it will actually use. The algorithm is quite long,
so it lives in the Haskell wiki at
https://gitlab.haskell.org/ghc/ghc/wikis/commentary/compiler/deriving-strategies
("The deriving strategy resolution algorithm" section).
Internally, GHC uses the DerivStrategy datatype to denote a user-requested
deriving strategy, and it uses the DerivSpecMechanism datatype to denote what
GHC will use to derive the instance after taking the above steps. In other
words, GHC will always settle on a DerivSpecMechnism, even if the user did not
ask for a particular DerivStrategy (using the algorithm linked to above). The four strategies do genuinely different things. stock generates real code
per class, known to the compiler. newtype coerces an existing instance through
a newtype (zero cost, since the representations are identical). anyclass takes
the class’s default methods. via coerces through a type you nominate.
For inferred contexts there is a further problem: what constraints should the
generated instance require? deriving Show for data T a = MkT a needs
Show a, but working that out in general means running the solver over the
generated code:
There are two sorts of 'deriving', as represented by the two constructors
for DerivContext:
* InferContext mb_wildcard: This can either be:
- The deriving clause for a data type.
(e.g, data T a = T1 a deriving( Eq ))
In this case, mb_wildcard = Nothing.
- A standalone declaration with an extra-constraints wildcard
(e.g., deriving instance _ => Eq (Foo a))
In this case, mb_wildcard = Just loc, where loc is the location
of the extra-constraints wildcard.
Here we must infer an instance context,
and generate instance declaration
instance Eq a => Eq (T a) where ...
* SupplyContext theta: standalone deriving
deriving instance Eq a => Eq (T a)
Here we only need to fill in the bindings;
the instance context (theta) is user-supplied
For the InferContext case, we must figure out the
instance context (inferConstraintsStock). Suppose we are inferring
the instance context for
C t1 .. tn (T s1 .. sm)
There are two cases Show the rest of this Note (34 more lines)
* (T s1 .. sm) :: * (the normal case)
Then we behave like Eq and guess (C t1 .. tn t)
for each data constructor arg of type t. More
details below.
* (T s1 .. sm) :: * -> * (the functor-like case)
Then we behave like Functor.
In both cases we produce a bunch of un-simplified constraints
and them simplify them in simplifyInstanceContexts; see
Note [Simplifying the instance context].
In the functor-like case, we may need to unify some kind variables with * in
order for the generated instance to be well-kinded. An example from #10524:
newtype Compose (f :: k2 -> *) (g :: k1 -> k2) (a :: k1)
= Compose (f (g a)) deriving Functor
Earlier in the deriving pipeline, GHC unifies the kind of Compose f g
(k1 -> *) with the kind of Functor's argument (* -> *), so k1 := *. But this
alone isn't enough, since k2 wasn't unified with *:
instance (Functor (f :: k2 -> *), Functor (g :: * -> k2)) =>
Functor (Compose f g) where ...
The two Functor constraints are ill-kinded. To ensure this doesn't happen, we:
1. Collect all of a datatype's subtypes which require functor-like
constraints.
2. For each subtype, create a substitution by unifying the subtype's kind
with (* -> *).
3. Compose all the substitutions into one, then apply that substitution to
all of the in-scope type variables and the instance types. Stock deriving also needs helper bindings (comparison tags, enumeration indices) that must not collide with anything the user wrote:
We often want to make top-level auxiliary bindings in derived instances.
For example, derived Ix instances sometimes generate code like this:
data T = ...
deriving instance Ix T
==>
instance Ix T where
range (a, b) = map tag2con_T [dataToTag# a .. dataToTag# b]
$tag2con_T :: Int -> T
$tag2con_T = ...code....
Note that multiple instances of the same type might need to use the same sort
of auxiliary binding. For example, $tag2con is used not only in derived Ix
instances, but also in derived Enum instances:
deriving instance Enum T
==>
Show the rest of this Note (192 more lines)
instance Enum T where
toEnum i = tag2con_T i
$tag2con_T :: Int -> T
$tag2con_T = ...code....
How do we ensure that the two usages of $tag2con_T do not conflict with each
other? We do so by generating a separate $tag2con_T definition for each
instance, giving each definition an Exact RdrName with a separate Unique to
avoid name clashes:
instance Ix T where
range (a, b) = map tag2con_T{Uniq2} [dataToTag# a .. dataToTag# b]
instance Enum T where
toEnum a = $tag2con_T{Uniq2} a
$tag2con_T{Uniq1} and $tag2con_T{Uniq2} are Exact RdrNames with
underlying System Names
$tag2con_T{Uniq1} :: Int -> T
$tag2con_T{Uniq1} = ...code....
$tag2con_T{Uniq2} :: Int -> T
$tag2con_T{Uniq2} = ...code....
Note that:
* This is /precisely/ the same mechanism that we use for
Template Haskell–generated code.
See Note [Binders in Template Haskell] in GHC.ThToHs.
There we explain why we use a 'System' flavour of the Name we generate.
* See "Wrinkle: Reducing code duplication" for how we can avoid generating
lots of duplicated code in common situations.
* See "Wrinkle: Why we sometimes do generated duplicate code" for why this
de-duplication mechanism isn't perfect, so we fall back to CSE
(which is very effective within a single module).
* Note that the "_T" part of "$tag2con_T" is just for debug-printing
purposes. We could call them all "$tag2con", or even just "aux".
The Unique is enough to keep them separate.
This is important: we might be generating an Eq instance for two
completely-distinct imported type constructors T.
At first glance, it might appear that this plan is infeasible, as it would
require generating multiple top-level declarations with the same OccName. But
what if auxiliary bindings /weren't/ top-level? Conceptually, we could imagine
that auxiliary bindings are /local/ to the instance declarations in which they
are used. Using some hypothetical Haskell syntax, it might look like this:
let {
$tag2con_T{Uniq1} :: Int -> T
$tag2con_T{Uniq1} = ...code....
$tag2con_T{Uniq2} :: Int -> T
$tag2con_T{Uniq2} = ...code....
} in {
instance Ix T where
range (a, b) = map tag2con_T{Uniq2} [dataToTag# a .. dataToTag# b]
instance Enum T where
toEnum a = $tag2con_T{Uniq2} a
}
Making auxiliary bindings local is key to making this work, since GHC will
not reject local bindings with duplicate names provided that:
* Each binding has a distinct unique, and
* Each binding has an Exact RdrName with a System Name.
Even though the hypothetical Haskell syntax above does not exist, we can
accomplish the same end result through some sleight of hand in renameDeriv:
we rename auxiliary bindings with rnLocalValBindsLHS. (If we had used
rnTopBindsLHS instead, then GHC would spuriously reject auxiliary bindings
with the same OccName as duplicates.) Luckily, no special treatment is needed
to typecheck them; we can typecheck them as normal top-level bindings
(using tcTopBinds) without danger.
Wrinkle: Reducing code duplication
While the approach of generating copies of each sort of auxiliary binder per
derived instance is simpler, it can lead to code bloat if done naïvely.
Consider this example:
data T = ...
deriving instance Eq T
deriving instance Ord T
==>
instance Ix T where
range (a, b) = map tag2con_T{Uniq2} [dataToTag# a .. dataToTag# b]
instance Enum T where
toEnum a = $tag2con_T{Uniq2} a
$tag2con_T{Uniq1} :: Int -> T
$tag2con_T{Uniq1} = ...code....
$tag2con_T{Uniq2} :: Int -> T
$tag2con_T{Uniq2} = ...code....
$tag2con_T{Uniq1} and $tag2con_T{Uniq2} are blatant duplicates of each other,
which is not ideal. Surely GHC can do better than that at the very least! And
indeed it does. Within the genAuxBinds function, GHC performs a small CSE-like
pass to define duplicate auxiliary binders in terms of the original one. On
the example above, that would look like this:
$tag2con_T{Uniq1} :: Int -> T
$tag2con_T{Uniq1} = ...code....
$tag2con_T{Uniq2} :: Int -> T
$tag2con_T{Uniq2} = $tag2con_T{Uniq1}
(Note that this pass does not cover all possible forms of code duplication.
See "Wrinkle: Why we sometimes do generate duplicate code" for situations
where genAuxBinds does not deduplicate code.)
To start, genAuxBinds is given a list of AuxBindSpecs, which describe the sort
of auxiliary bindings that must be generates along with their RdrNames. As
genAuxBinds processes this list, it marks the first occurrence of each sort of
auxiliary binding as the "original". For example, if genAuxBinds sees a
DerivCon2Tag for the first time (with the RdrName $tag2con_T{Uniq1}), then it
will generate the full code for a $tag2con binding:
$tag2con_T{Uniq1} :: Int -> T
$tag2con_T{Uniq1} = ...code....
Later, if genAuxBinds sees any additional DerivCon2Tag values, it will treat
them as duplicates. For example, if genAuxBinds later sees a DerivCon2Tag with
the RdrName $tag2con_T{Uniq2}, it will generate this code, which is much more
compact:
$tag2con_T{Uniq2} :: Int -> T
$tag2con_T{Uniq2} = $tag2con_T{Uniq1}
An alternative approach would be /not/ performing any kind of deduplication in
genAuxBinds at all and simply relying on GHC's simplifier to perform this kind
of CSE. But this is a more expensive analysis in general, while genAuxBinds can
accomplish the same result with a simple check.
Wrinkle: Why we sometimes do generate duplicate code
It is worth noting that deduplicating auxiliary binders is difficult in the
general case. Here are two particular examples where GHC cannot easily remove
duplicate copies of an auxiliary binding:
1. When derived instances are contained in different modules, as in the
following example:
module A where
data T = ...
module B where
import A
deriving instance Ix T
module C where
import B
deriving instance Enum T
The derived Eq and Enum instances for T make use of $tag2con_T, and since
they are defined in separate modules, each module must produce its own copy
of $tag2con_T.
2. When derived instances are separated by TH splices (#18321), as in the
following example:
module M where
data T = ...
deriving instance Ix T
$(pure [])
deriving instance Enum T
Due to the way that GHC typechecks TyClGroups, genAuxBinds will run twice
in this program: once for all the declarations before the TH splice, and
once again for all the declarations after the TH splice. As a result,
$tag2con_T will be generated twice, since genAuxBinds will be unable to
recognize the presence of duplicates.
These situations are much rarer, so we do not spend any effort to deduplicate
auxiliary bindings there. Instead, we focus on the common case of multiple
derived instances within the same module, not separated by any TH splices.
(This is the case described in "Wrinkle: Reducing code duplication".) In
situation (1), we can at least fall back on GHC's simplifier to pick up
genAuxBinds' slack. Seeing it happen
What you wrote.
-- | Type classes are the clearest demonstration that Core is a *different*
-- language from Haskell, not a lightly-desugared version of it.
--
-- Watch `describe` acquire an extra argument between the typechecked output and
-- the desugared Core: the class constraint `Show a =>` becomes an ordinary
-- value parameter holding a dictionary of methods.
module Classes where
class Container f where
empty :: f a
insert :: a -> f a -> f a
newtype Stack a = Stack [a]
instance Container Stack where
empty = Stack []
insert x (Stack xs) = Stack (x : xs)
describe :: Show a => a -> String
describe x = "value: " ++ show x
twice :: Container f => a -> f a -> f a
twice x c = insert x (insert x c)The elaborated program: AbsBinds wrappers, dictionary arguments at call sites, and Evidence lines naming the instance each solved constraint resolved to.
$tcStack
= TyCon
3763377625761546056#Word64 14659598628851987569#Word64 $trModule
(TrNameS "Stack"#) 0# krep$*Arr*
$tc'Stack
= TyCon
11353571921778005947#Word64 8228856247415007804#Word64 $trModule
(TrNameS "'Stack"#) 1# $krep
$tcContainer
= TyCon
4419330194507431766#Word64 15496829273379683885#Word64 $trModule
(TrNameS "Container"#) 0# $krep
$krep = KindRepVar 0
$krep = KindRepFun krep$*Arr* krep$Constraint
$krep = KindRepFun $krep $krep
$krep = KindRepTyConApp $tcList ((:) @KindRep $krep [] @KindRep)
$krep = KindRepTyConApp $tcStack ((:) @KindRep $krep [] @KindRep)
$trModule = Module (TrNameS "main"#) (TrNameS "Classes"#)
AbsBinds [] []
{Exports: [twice <= twice
wrap: <>]
Exported types: twice
:: forall (f :: * -> *) a. Container f => a -> f a -> f a
Binds: twice x c
= insert @f $dContainer @a x (insert @f $dContainer @a x c)
Evidence: [EvBinds{}]}
AbsBinds [] []
{Exports: [describe <= describe
wrap: <>]
Exported types: describe :: forall a. Show a => a -> String
Binds: describe x = "value: " ++ show x
Evidence: [EvBinds{}]}
AbsBinds [] []
{Exports: [$fContainerStack <= $dContainer
wrap: <>]
Exported types: $fContainerStack :: Container Stack
Binds: $dContainer = C:Container @Stack $cempty $cinsert
Evidence: [EvBinds{}]}
AbsBinds [] []
{Exports: [$cempty <= empty
wrap: <>]
Exported types: $cempty :: forall a. Stack a
Binds: AbsBinds [] []
{Exports: [empty <= empty
wrap: <>]
Exported types: empty :: forall a. Stack a
Binds: empty = Stack @a [] @a
Evidence: [EvBinds{}]}
Evidence: [EvBinds{}]}
AbsBinds [] []
{Exports: [$cinsert <= insert
wrap: <>]
Exported types: $cinsert :: forall a. a -> Stack a -> Stack a
Binds: AbsBinds [] []
{Exports: [insert <= insert
wrap: <>]
Exported types: insert :: forall a. a -> Stack a -> Stack a
Binds: insert x (Stack{EvBinds{}} xs) = Stack @a (x : xs)
Evidence: [EvBinds{}]}
Evidence: [EvBinds{}]}Classes.$tcStack
= GHC.Internal.Types.TyCon
3763377625761546056#Word64 14659598628851987569#Word64
Classes.$trModule (GHC.Internal.Types.TrNameS "Stack"#) 0#
GHC.Internal.Types.krep$*Arr*
Classes.$tc'Stack
= GHC.Internal.Types.TyCon
11353571921778005947#Word64 8228856247415007804#Word64
Classes.$trModule (GHC.Internal.Types.TrNameS "'Stack"#) 1# $krep
Classes.$tcContainer
= GHC.Internal.Types.TyCon
4419330194507431766#Word64 15496829273379683885#Word64
Classes.$trModule (GHC.Internal.Types.TrNameS "Container"#) 0#
$krep
$krep [InlPrag=[~]] = GHC.Internal.Types.KindRepVar 0
$krep [InlPrag=[~]]
= GHC.Internal.Types.KindRepFun
GHC.Internal.Types.krep$*Arr* GHC.Internal.Types.krep$Constraint
$krep [InlPrag=[~]] = GHC.Internal.Types.KindRepFun $krep $krep
$krep [InlPrag=[~]]
= GHC.Internal.Types.KindRepTyConApp
GHC.Internal.Types.$tcList
((:) @GHC.Internal.Types.KindRep
$krep [] @GHC.Internal.Types.KindRep)
$krep [InlPrag=[~]]
= GHC.Internal.Types.KindRepTyConApp
Classes.$tcStack
((:) @GHC.Internal.Types.KindRep
$krep [] @GHC.Internal.Types.KindRep)
Classes.$trModule
= GHC.Internal.Types.Module
(GHC.Internal.Types.TrNameS "main"#)
(GHC.Internal.Types.TrNameS "Classes"#)
AbsBinds [] []
{Exports: [twice <= twice
wrap: <>]
Exported types: twice
:: forall (f :: * -> *) a. Container f => a -> f a -> f a
[LclId]
Binds: twice x c
= insert @f $dContainer @a x (insert @f $dContainer @a x c)
Evidence: [EvBinds{}]}
AbsBinds [] []
{Exports: [describe <= describe
wrap: <>]
Exported types: describe :: forall a. Show a => a -> String
[LclId]
Binds: describe x = "value: " ++ show x
Evidence: [EvBinds{}]}
AbsBinds [] []
{Exports: [Classes.$fContainerStack <= $dContainer
wrap: <>]
Exported types: Classes.$fContainerStack [InlPrag=CONLIKE]
:: Container Stack
[LclIdX[DFunId],
Unf=DFun: \ -> Classes.C:Container TYPE: Stack $cempty $cinsert]
Binds: $dContainer = Classes.C:Container @Stack $cempty $cinsert
Evidence: [EvBinds{}]}
AbsBinds [] []
{Exports: [$cempty <= empty
wrap: <>]
Exported types: $cempty :: forall a. Stack a
[LclId]
Binds: AbsBinds [] []
{Exports: [empty <= empty
wrap: <>]
Exported types: empty :: forall a. Stack a
[LclId]
Binds: empty = Stack @a [] @a
Evidence: [EvBinds{}]}
Evidence: [EvBinds{}]}
AbsBinds [] []
{Exports: [$cinsert <= insert
wrap: <>]
Exported types: $cinsert :: forall a. a -> Stack a -> Stack a
[LclId]
Binds: AbsBinds [] []
{Exports: [insert <= insert
wrap: <>]
Exported types: insert :: forall a. a -> Stack a -> Stack a
[LclId]
Binds: insert x (Stack{EvBinds{}} xs) = Stack @a (x : xs)
Evidence: [EvBinds{}]}
Evidence: [EvBinds{}]}All of Haskell reduced to Core, before any optimisation.
Result size of Desugar (after optimization)
= {terms: 91, types: 60, coercions: 10, joins: 0/0}
-- RHS size: {terms: 6, types: 5, coercions: 5, joins: 0/0}
$cinsert :: forall a. a -> Stack a -> Stack a
$cinsert
= \ (@a) (x :: a) (ds :: Stack a) ->
(: x (ds `cast` <Co:2> :: Stack a ~R# [a]))
`cast` <Co:3> :: [a] ~R# Stack a
-- RHS size: {terms: 3, types: 1, coercions: 5, joins: 0/0}
$fContainerStack :: Container Stack
$fContainerStack
= C:Container
([] `cast` <Co:5> :: (forall a. [a]) ~R# (forall a. Stack a))
$cinsert
-- RHS size: {terms: 9, types: 6, coercions: 0, joins: 0/0}
describe :: forall a. Show a => a -> String
describe
= \ (@a) ($dShow :: Show a) (x :: a) ->
++ (unpackCString# "value: "#) (show $dShow x)
-- RHS size: {terms: 12, types: 12, coercions: 0, joins: 0/0}
twice :: forall (f :: * -> *) a. Container f => a -> f a -> f a
twice
= \ (@(f :: * -> *))
(@a)
($dContainer :: Container f)
(x :: a)
(c :: f a) ->
insert $dContainer x (insert $dContainer x c)
-- RHS size: {terms: 5, types: 0, coercions: 0, joins: 0/0}
$trModule :: Module
$trModule = Module (TrNameS "main"#) (TrNameS "Classes"#)
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$krep :: KindRep
$krep = KindRepFun krep$*Arr* krep$Constraint
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$krep :: KindRep
$krep = $WKindRepVar (I# 0#)
-- RHS size: {terms: 5, types: 2, coercions: 0, joins: 0/0}
$krep :: KindRep
$krep = KindRepTyConApp $tcList (: $krep [])
-- RHS size: {terms: 8, types: 0, coercions: 0, joins: 0/0}
$tcContainer :: TyCon
$tcContainer
= TyCon
4419330194507431766#Word64
15496829273379683885#Word64
$trModule
(TrNameS "Container"#)
0#
$krep
-- RHS size: {terms: 8, types: 0, coercions: 0, joins: 0/0}
$tcStack :: TyCon
$tcStack
= TyCon
3763377625761546056#Word64
14659598628851987569#Word64
$trModule
(TrNameS "Stack"#)
0#
krep$*Arr*
-- RHS size: {terms: 5, types: 2, coercions: 0, joins: 0/0}
$krep :: KindRep
$krep = KindRepTyConApp $tcStack (: $krep [])
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$krep :: KindRep
$krep = KindRepFun $krep $krep
-- RHS size: {terms: 8, types: 0, coercions: 0, joins: 0/0}
$tc'Stack :: TyCon
$tc'Stack
= TyCon
11353571921778005947#Word64
8228856247415007804#Word64
$trModule
(TrNameS "'Stack"#)
1#
$krepResult size of Desugar (after optimization)
= {terms: 91, types: 60, coercions: 10, joins: 0/0}
-- RHS size: {terms: 6, types: 5, coercions: 5, joins: 0/0}
$cinsert :: forall a. a -> Stack a -> Stack a
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=2,unsat_ok=True,boring_ok=True)}]
$cinsert
= \ (@a) (x :: a) (ds :: Stack a) ->
(GHC.Internal.Types.:
@a x (ds `cast` (Classes.N:Stack <a>_N :: Stack a ~R# [a])))
`cast` (Sym Classes.N:Stack <a>_N :: [a] ~R# Stack a)
-- RHS size: {terms: 3, types: 1, coercions: 5, joins: 0/0}
Classes.$fContainerStack [InlPrag=CONLIKE] :: Container Stack
[LclIdX[DFunId],
Unf=DFun: \ ->
Classes.C:Container TYPE: Stack
GHC.Internal.Types.[]
`cast` (forall (a :: <*>_N). Sym Classes.N:Stack <a>_N
:: (forall a. [a]) ~R# (forall a. Stack a))
$cinsert]
Classes.$fContainerStack
= Classes.C:Container
@Stack
(GHC.Internal.Types.[]
`cast` (forall (a :: <*>_N). Sym Classes.N:Stack <a>_N
:: (forall a. [a]) ~R# (forall a. Stack a)))
$cinsert
-- RHS size: {terms: 9, types: 6, coercions: 0, joins: 0/0}
describe :: forall a. Show a => a -> String
[LclIdX,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [30 0] 110 0}]
describe
= \ (@a) ($dShow :: Show a) (x :: a) ->
++
@Char
(GHC.Internal.CString.unpackCString# "value: "#)
(show @a $dShow x)
-- RHS size: {terms: 12, types: 12, coercions: 0, joins: 0/0}
twice :: forall (f :: * -> *) a. Container f => a -> f a -> f a
[LclIdX,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [60 0 0] 80 0}]
twice
= \ (@(f :: * -> *))
(@a)
($dContainer :: Container f)
(x :: a)
(c :: f a) ->
insert @f $dContainer @a x (insert @f $dContainer @a x c)
-- RHS size: {terms: 5, types: 0, coercions: 0, joins: 0/0}
Classes.$trModule :: GHC.Internal.Types.Module
[LclIdX,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 80 10}]
Classes.$trModule
= GHC.Internal.Types.Module
(GHC.Internal.Types.TrNameS "main"#)
(GHC.Internal.Types.TrNameS "Classes"#)
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$krep [InlPrag=[~]] :: GHC.Internal.Types.KindRep
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
$krep
= GHC.Internal.Types.KindRepFun
GHC.Internal.Types.krep$*Arr* GHC.Internal.Types.krep$Constraint
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$krep [InlPrag=[~]] :: GHC.Internal.Types.KindRep
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=False, ConLike=True, WorkFree=False, Expandable=True,
Guidance=IF_ARGS [] 30 0}]
$krep = GHC.Internal.Types.$WKindRepVar (GHC.Internal.Types.I# 0#)
-- RHS size: {terms: 5, types: 2, coercions: 0, joins: 0/0}
$krep [InlPrag=[~]] :: GHC.Internal.Types.KindRep
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 20 10}]
$krep
= GHC.Internal.Types.KindRepTyConApp
GHC.Internal.Types.$tcList
(GHC.Internal.Types.:
@GHC.Internal.Types.KindRep
$krep
(GHC.Internal.Types.[] @GHC.Internal.Types.KindRep))
-- RHS size: {terms: 8, types: 0, coercions: 0, joins: 0/0}
Classes.$tcContainer :: GHC.Internal.Types.TyCon
[LclIdX,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 60 10}]
Classes.$tcContainer
= GHC.Internal.Types.TyCon
4419330194507431766#Word64
15496829273379683885#Word64
Classes.$trModule
(GHC.Internal.Types.TrNameS "Container"#)
0#
$krep
-- RHS size: {terms: 8, types: 0, coercions: 0, joins: 0/0}
Classes.$tcStack :: GHC.Internal.Types.TyCon
[LclIdX,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 50 10}]
Classes.$tcStack
= GHC.Internal.Types.TyCon
3763377625761546056#Word64
14659598628851987569#Word64
Classes.$trModule
(GHC.Internal.Types.TrNameS "Stack"#)
0#
GHC.Internal.Types.krep$*Arr*
-- RHS size: {terms: 5, types: 2, coercions: 0, joins: 0/0}
$krep [InlPrag=[~]] :: GHC.Internal.Types.KindRep
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 20 10}]
$krep
= GHC.Internal.Types.KindRepTyConApp
Classes.$tcStack
(GHC.Internal.Types.:
@GHC.Internal.Types.KindRep
$krep
(GHC.Internal.Types.[] @GHC.Internal.Types.KindRep))
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$krep [InlPrag=[~]] :: GHC.Internal.Types.KindRep
[LclId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
$krep = GHC.Internal.Types.KindRepFun $krep $krep
-- RHS size: {terms: 8, types: 0, coercions: 0, joins: 0/0}
Classes.$tc'Stack :: GHC.Internal.Types.TyCon
[LclIdX,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 50 10}]
Classes.$tc'Stack
= GHC.Internal.Types.TyCon
11353571921778005947#Word64
8228856247415007804#Word64
Classes.$trModule
(GHC.Internal.Types.TrNameS "'Stack"#)
1#
$krepThe same program after the simplifier has run.
Result size of Tidy Core
= {terms: 111, types: 70, coercions: 19, joins: 0/0}
-- RHS size: {terms: 6, types: 5, coercions: 2, joins: 0/0}
$fContainerStack1 :: forall a. a -> Stack a -> [a]
$fContainerStack1
= \ (@a) (x :: a) (ds :: Stack a) ->
: x (ds `cast` <Co:2> :: Stack a ~R# [a])
-- RHS size: {terms: 3, types: 1, coercions: 17, joins: 0/0}
$fContainerStack :: Container Stack
$fContainerStack
= C:Container
([] `cast` <Co:5> :: (forall a. [a]) ~R# (forall a. Stack a))
($fContainerStack1
`cast` <Co:12> :: (forall a. a -> Stack a -> [a])
~R# (forall a. a -> Stack a -> Stack a))
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
describe1 :: Addr#
describe1 = "value: "#
-- RHS size: {terms: 8, types: 5, coercions: 0, joins: 0/0}
describe :: forall a. Show a => a -> String
describe
= \ (@a) ($dShow :: Show a) (x :: a) ->
unpackAppendCString# describe1 (show $dShow x)
-- RHS size: {terms: 12, types: 12, coercions: 0, joins: 0/0}
twice :: forall (f :: * -> *) a. Container f => a -> f a -> f a
twice
= \ (@(f :: * -> *))
(@a)
($dContainer :: Container f)
(x :: a)
(c :: f a) ->
insert $dContainer x (insert $dContainer x c)
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$trModule4 :: Addr#
$trModule4 = "main"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$trModule3 :: TrName
$trModule3 = TrNameS $trModule4
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$trModule2 :: Addr#
$trModule2 = "Classes"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$trModule1 :: TrName
$trModule1 = TrNameS $trModule2
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$trModule :: Module
$trModule = Module $trModule3 $trModule1
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$tcContainer1 :: KindRep
$tcContainer1 = KindRepFun krep$*Arr* krep$Constraint
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$krep :: KindRep
$krep = KindRepVar 0#
-- RHS size: {terms: 3, types: 2, coercions: 0, joins: 0/0}
$krep1 :: [KindRep]
$krep1 = : $krep []
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$krep2 :: KindRep
$krep2 = KindRepTyConApp $tcList $krep1
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$tcContainer3 :: Addr#
$tcContainer3 = "Container"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$tcContainer2 :: TrName
$tcContainer2 = TrNameS $tcContainer3
-- RHS size: {terms: 7, types: 0, coercions: 0, joins: 0/0}
$tcContainer :: TyCon
$tcContainer
= TyCon
4419330194507431766#Word64
15496829273379683885#Word64
$trModule
$tcContainer2
0#
$tcContainer1
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$tcStack2 :: Addr#
$tcStack2 = "Stack"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$tcStack1 :: TrName
$tcStack1 = TrNameS $tcStack2
-- RHS size: {terms: 7, types: 0, coercions: 0, joins: 0/0}
$tcStack :: TyCon
$tcStack
= TyCon
3763377625761546056#Word64
14659598628851987569#Word64
$trModule
$tcStack1
0#
krep$*Arr*
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$krep3 :: KindRep
$krep3 = KindRepTyConApp $tcStack $krep1
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$tc'Stack1 :: KindRep
$tc'Stack1 = KindRepFun $krep2 $krep3
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$tc'Stack3 :: Addr#
$tc'Stack3 = "'Stack"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$tc'Stack2 :: TrName
$tc'Stack2 = TrNameS $tc'Stack3
-- RHS size: {terms: 7, types: 0, coercions: 0, joins: 0/0}
$tc'Stack :: TyCon
$tc'Stack
= TyCon
11353571921778005947#Word64
8228856247415007804#Word64
$trModule
$tc'Stack2
1#
$tc'Stack1Result size of Tidy Core
= {terms: 111, types: 70, coercions: 19, joins: 0/0}
-- RHS size: {terms: 6, types: 5, coercions: 2, joins: 0/0}
Classes.$fContainerStack1 :: forall a. a -> Stack a -> [a]
[GblId,
Arity=2,
Str=<L><L>,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=2,unsat_ok=True,boring_ok=True)}]
Classes.$fContainerStack1
= \ (@a) (x :: a) (ds :: Stack a) ->
GHC.Internal.Types.:
@a x (ds `cast` (Classes.N:Stack <a>_N :: Stack a ~R# [a]))
-- RHS size: {terms: 3, types: 1, coercions: 17, joins: 0/0}
Classes.$fContainerStack [InlPrag=CONLIKE] :: Container Stack
[GblId[DFunId],
Unf=DFun: \ ->
Classes.C:Container TYPE: Stack
GHC.Internal.Types.[]
`cast` (forall (a :: <*>_N). Sym Classes.N:Stack <a>_N
:: (forall a. [a]) ~R# (forall a. Stack a))
Classes.$fContainerStack1
`cast` (forall (a :: <*>_N).
<a>_R
%<Many>_N ->_R <Stack a>_R
%<Many>_N ->_R Sym Classes.N:Stack <a>_N
:: (forall a. a -> Stack a -> [a])
~R# (forall a. a -> Stack a -> Stack a))]
Classes.$fContainerStack
= Classes.C:Container
@Stack
(GHC.Internal.Types.[]
`cast` (forall (a :: <*>_N). Sym Classes.N:Stack <a>_N
:: (forall a. [a]) ~R# (forall a. Stack a)))
(Classes.$fContainerStack1
`cast` (forall (a :: <*>_N).
<a>_R
%<Many>_N ->_R <Stack a>_R
%<Many>_N ->_R Sym Classes.N:Stack <a>_N
:: (forall a. a -> Stack a -> [a])
~R# (forall a. a -> Stack a -> Stack a)))
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
Classes.describe1 :: GHC.Internal.Prim.Addr#
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 30 0}]
Classes.describe1 = "value: "#
-- RHS size: {terms: 8, types: 5, coercions: 0, joins: 0/0}
describe :: forall a. Show a => a -> String
[GblId,
Arity=2,
Str=<MP(A,1C(1,L),A)><L>,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [30 0] 60 0}]
describe
= \ (@a) ($dShow :: Show a) (x :: a) ->
GHC.Internal.CString.unpackAppendCString#
Classes.describe1 (show @a $dShow x)
-- RHS size: {terms: 12, types: 12, coercions: 0, joins: 0/0}
twice :: forall (f :: * -> *) a. Container f => a -> f a -> f a
[GblId,
Arity=3,
Str=<SP(A,SC(S,C(1,L)))><L><L>,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [60 0 0] 80 0}]
twice
= \ (@(f :: * -> *))
(@a)
($dContainer :: Container f)
(x :: a)
(c :: f a) ->
insert @f $dContainer @a x (insert @f $dContainer @a x c)
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
Classes.$trModule4 :: GHC.Internal.Prim.Addr#
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 20 0}]
Classes.$trModule4 = "main"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
Classes.$trModule3 :: GHC.Internal.Types.TrName
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Classes.$trModule3 = GHC.Internal.Types.TrNameS Classes.$trModule4
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
Classes.$trModule2 :: GHC.Internal.Prim.Addr#
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 30 0}]
Classes.$trModule2 = "Classes"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
Classes.$trModule1 :: GHC.Internal.Types.TrName
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Classes.$trModule1 = GHC.Internal.Types.TrNameS Classes.$trModule2
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
Classes.$trModule :: GHC.Internal.Types.Module
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Classes.$trModule
= GHC.Internal.Types.Module Classes.$trModule3 Classes.$trModule1
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
Classes.$tcContainer1 [InlPrag=[~]] :: GHC.Internal.Types.KindRep
[GblId, Unf=OtherCon []]
Classes.$tcContainer1
= GHC.Internal.Types.KindRepFun
GHC.Internal.Types.krep$*Arr* GHC.Internal.Types.krep$Constraint
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$krep :: GHC.Internal.Types.KindRep
[GblId, Unf=OtherCon []]
$krep = GHC.Internal.Types.KindRepVar 0#
-- RHS size: {terms: 3, types: 2, coercions: 0, joins: 0/0}
$krep1 :: [GHC.Internal.Types.KindRep]
[GblId, Unf=OtherCon []]
$krep1
= GHC.Internal.Types.:
@GHC.Internal.Types.KindRep
$krep
(GHC.Internal.Types.[] @GHC.Internal.Types.KindRep)
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$krep2 :: GHC.Internal.Types.KindRep
[GblId, Unf=OtherCon []]
$krep2
= GHC.Internal.Types.KindRepTyConApp
GHC.Internal.Types.$tcList $krep1
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
Classes.$tcContainer3 :: GHC.Internal.Prim.Addr#
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 40 0}]
Classes.$tcContainer3 = "Container"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
Classes.$tcContainer2 :: GHC.Internal.Types.TrName
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Classes.$tcContainer2
= GHC.Internal.Types.TrNameS Classes.$tcContainer3
-- RHS size: {terms: 7, types: 0, coercions: 0, joins: 0/0}
Classes.$tcContainer :: GHC.Internal.Types.TyCon
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Classes.$tcContainer
= GHC.Internal.Types.TyCon
4419330194507431766#Word64
15496829273379683885#Word64
Classes.$trModule
Classes.$tcContainer2
0#
Classes.$tcContainer1
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
Classes.$tcStack2 :: GHC.Internal.Prim.Addr#
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 30 0}]
Classes.$tcStack2 = "Stack"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
Classes.$tcStack1 :: GHC.Internal.Types.TrName
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Classes.$tcStack1 = GHC.Internal.Types.TrNameS Classes.$tcStack2
-- RHS size: {terms: 7, types: 0, coercions: 0, joins: 0/0}
Classes.$tcStack :: GHC.Internal.Types.TyCon
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Classes.$tcStack
= GHC.Internal.Types.TyCon
3763377625761546056#Word64
14659598628851987569#Word64
Classes.$trModule
Classes.$tcStack1
0#
GHC.Internal.Types.krep$*Arr*
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$krep3 :: GHC.Internal.Types.KindRep
[GblId, Unf=OtherCon []]
$krep3 = GHC.Internal.Types.KindRepTyConApp Classes.$tcStack $krep1
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
Classes.$tc'Stack1 [InlPrag=[~]] :: GHC.Internal.Types.KindRep
[GblId, Unf=OtherCon []]
Classes.$tc'Stack1 = GHC.Internal.Types.KindRepFun $krep2 $krep3
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
Classes.$tc'Stack3 :: GHC.Internal.Prim.Addr#
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 30 0}]
Classes.$tc'Stack3 = "'Stack"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
Classes.$tc'Stack2 :: GHC.Internal.Types.TrName
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Classes.$tc'Stack2 = GHC.Internal.Types.TrNameS Classes.$tc'Stack3
-- RHS size: {terms: 7, types: 0, coercions: 0, joins: 0/0}
Classes.$tc'Stack :: GHC.Internal.Types.TyCon
[GblId,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [] 10 10}]
Classes.$tc'Stack
= GHC.Internal.Types.TyCon
11353571921778005947#Word64
8228856247415007804#Word64
Classes.$trModule
Classes.$tc'Stack2
1#
Classes.$tc'Stack1Look for $dContainer in twice. The constraint Container f => in the source
has become a value parameter, and the two insert calls select from it.
Then compare Core (optimised): for a known concrete type the simplifier can often see which dictionary arrives, inline the selection, and leave a direct call. That is the specialiser’s doing, and the reason class-heavy Haskell is not automatically slow.
Reading the source yourself
GHC/Core/Class.hs: small, and shows exactly what a class is once elaborated. Start here; everything else is easier afterwards.GHC/Tc/Instance/Class.hsfor matching a Wanted against instances.GHC/Tc/TyCl/Instance.hsfor what an instance declaration turns into, superclass evidence included.GHC/Tc/Deriv/*last.Deriv.hschooses the strategy,Deriv/Generate.hswrites the code,Deriv/Infer.hsworks out the context.
-ddump-deriv shows exactly what deriving generated, which is the quickest way
to understand a confusing error in code you did not write.