简体   繁体   中英

Why does the following type constructor take less arguments than the data constructor?

I have the following type declaration:

data Quadruple a b = Quad a a b b

(Some of you might recognize this declaration from Yet Another Haskell Tutorial ).

I understand the code's intent to be: create a data type called Quadruple. It has a single type constructor called Quad, which takes four values. The first two values must have the same type, and the last two values must have the same type.

I am having trouble explaining to myself why the type constructor takes less arguments than the data constructor. The only explanation I could conjure up is that Haskell is able to infer from the type declaration that if the first value is of type a, so is the second value, and if the third value is of type b, so is the fourth value.

Is this correct? Am I missing something here? I know Haskell has type inference so this makes sense, but I don't want to run with the wrong mental model. (Second time trying to learn Haskell).

It might be clearer with using the type

q = Quad 1 2 "peter" "paul"

Here you can see there are 2 types in play, Int and String . So q is of the type Quadruple Int String .

So there are 2 types in play: the number of type parameters of Quadruple . And there are 4 values(/fields) needed for the value constructor: the number of arguments of the Quad value constructor.

It's like with functions, where each formal parameter can be used more than once, like sqr x = x * x .

Same here: Quadruple is a type constructor: it constructs a new type when given two argument types, a and b . So Quadruple Int Double is a type; Quadruple Int Int is another type.

How its values are created? By using a data constructor Quad with four argument values: a pair with one type and another pair with another type. This is what you are telling to Haskell with that definition. So yes, it will use this knowledge in inferring and checking the types.

I think you are right - the left hand side only declares the type-variables that can be used on the right hand side 1 .

So all of the following are valid:

data A = A Int

has no variable on the LHS => none on the RHS

data B b = B1 Int | B2 b

has one variable on the LHS and uses this in one of it's constructors on the RHS; note you don't need to use the variable at all - this is called phantom type.

data C c = C Int

which is still something useful.

1 : except for existential types

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM