For a uni assignment, we have been given a line of Haskell code which shows:
newtype TC a = TC ([Id] -> Either TypeError ([Id], a))
Firstly, TypeError is something which needs to be implemented by us for the assignment so I can't post the data declaration here, but my question is this. How do I read the code above? What is the a right after the newtype TC? I also don't understand how TC is being reused to the right of the equals sign.
I think a here is a type variable since newtype works similarly to data. I don't know how knowing this will help my understanding.
CodePudding user response:
What is the
aright after thenewtype TC?
The a in the newtype declaration
newtype TC a = ...
expresses much the same as the x in a function declaration
f x = ...
a is a type parameter. So you'll be able to use TC as, for example, TC Int or TC Bool, similar to how you're able to use f like f 1 or f "bla bla" (depending on its type).
The case TC Int is equivalent to the following alternative:
newtype TCInt = TCInt ([Id] -> Either TypeError ([Id], Int))
I also don't understand how
TCis being reused to the right of the equals sign.
That is a bit of a confusing quirk in Haskell. Actually TC is not reused, rather you're declaring two separate entities which are both called TC. You could also call them differently:
newtype TC_T a = TC_V ([Id] -> Either TypeError ([Id], a))
TC_Tis a type constructor. This is the thing that will appear in type signatures, i.e.TC_T IntorTC_T Bool.TC_Vis a value constructor. You use this when, well, generating values of typeTC_T IntorTC_T Bool.
So for example, you might write these:
tci :: TC_T Int
tci = TC_V (\ids -> Right (ids, 37))
tcb :: TC_T Bool
tcb = TC_V (\ids -> Right (reverse ids, False))
With your original version it looks like this:
tci :: TC Int
tci = TC (\ids -> Right (ids, 37))
tcb :: TC Bool
tcb = TC (\ids -> Right (reverse ids, False))
...but it's still two separate things both called TV here. Most newtypes in Haskell call the type- and value constructors the same, but often only the type constructor is exported from a module and the value constructor left as an implementation detail.
Most of this is the same for data as for newtype. You could as well have
data TC a = TC ([Id] -> Either TypeError ([Id], a))
... the only difference to the newtype version is a subtle indirection: if it's data, then the compiler inserts an indirection which allows a bit more lazyness, but in this case there's hardly a point to doing that.
In practice, you generally use data only when you need multiple constructors and/or a constructor with multiple fields, which newtype does not support.
