18
votes

If I have a type like data T = T Int String and a function like so:

identity :: T -> T
identity (T a b) = T a b

After the deconstruction in the pattern matching, does GHC create a new T object containing references to the same Int and String? Or does it return the exact same object (with the same memory address) it received? I understand they're semantically equivalent, I'm just curious.

2

2 Answers

16
votes

In general GHC will allocate a new value rather than reuse the argument in that situation. In this particular case you could write something like

f :: T -> T
f t@(T x y) = t

to explicitly reuse the argument. Unfortunately, in one of the cases where you'd really want this --

fmap :: (a -> b) -> Either e a -> Either e b
fmap f (Right x) = Right (f x)
fmap f (Left x) = Left x

-- GHC will allocate a new Left value, and you can't simply reuse the argument, because the result has a different type. As far as I know there's no way to tell GHC to reuse the argument in this case other than unsafeCoerce.

15
votes

You can test this easily enough with -ddump-simpl. An allocation of a value of an ADT will show up as an application of a data constructor.

In this case, GHC does spot that it can reuse the value, and even that it doesn't have to perform the pattern matching:

module I where

data T = T Int String

identity :: T -> T
identity (T a b) = T a b

-

rwbarton@morphism:/tmp$ ghc -ddump-simpl I
[1 of 1] Compiling I                ( I.hs, I.o )

==================== Tidy Core ====================
Result size of Tidy Core = {terms: 3, types: 3, coercions: 0}

I.identity :: I.T -> I.T
[GblId, Arity=1, Caf=NoCafRefs, Str=DmdType]
I.identity = \ (ds_dHN :: I.T) -> ds_dHN

This happens even without optimizations enabled, and it also works on ADTs with more than one constructor.