• No results found

We will approach an understanding of pattern matches in stages, working through three examples of increasing complexity. All these examples will work over the somewhat hackneyed length-indexed vectors for simplicity and familiarity.

4.3.1

A simple pattern match

Naturally, Dependent Haskell retains the capability for simple pattern matches: -- isEmpty ::Vec a n→Bool

isEmpty v =casev of Nil →True

→False

A simple pattern match looks at a scrutinee—in this case, v—and chooses a case

alternative depending on the value of the scrutinee. The bodies of thecasealternatives need no extra information to be well typed. In this case, every body is clearly a Bool, with no dependency on which case has been chosen. Indeed, swapping the bodies would yield a well typed pattern match, too. In a simple pattern match, no type signature is required.33

4.3.2

A GADT pattern match

Today’s Haskell (and Dependent Haskell) supports GADT pattern-matches, where learning about the constructor that forms a scrutinee’s value can affect the types in a

casealternative body. Here is the example:

pred ::Nat →Nat

pred Zero =error "pred Zero"

pred (Succ n) = n

safeTail ::Vec a n→Either (n:∼: ’Zero) (Vec a(’pred n))

safeTail Nil =Left Refl safeTail ( :>t) =Right t

In this example, we must use type information learned through the pattern match in order for the body of the pattern match to type-check. (Here, and in the last example, I use the more typical syntax of defining a function via pattern matching. The reasoning is the same as if I had used an explicit case.) Let’s examine the two pattern match bodies individually:

• For Left Refl to be well typed at Either (n:∼: ’Zero)τ, we need to know that n

is indeed ’Zero. This fact is known only because we have pattern-matched on

Nil. Note that the type of Nil is Vec a ’Zero. Because we have discovered that our argument of type Vec a n isNil::Vec a’Zero, it must be that n ∼ ’Zero, as desired.

33Expert readers may be puzzled why this example is accepted without a type signature. After all,

pattern-matching againstNil indeeddoes introduce a type equality, making the result type of the match hard to infer. In this case, however, the existence of the last pattern, , which introduces no equalities, allows the return type to be inferred asBool.

• For Right t to be well typed atEither τ (Vec a(’pred n)) (wheret::Vec a n’ for somen’), we need to know thatn ∼ ’Succ n’, so that we can simplify ’pred n

to ’pred (’Succ n’) to n’. The equality n ∼ ’Succ n’ is exactly what we get by pattern-matching on:>.

Note that I have provided a type signature for safeTail. This is necessary in the event of a GADT pattern match, because there is no way, in general, to infer the return type of a pattern match where each branch has a type equality in scope.34

4.3.3

Dependent pattern match

New to Dependent Haskell is the dependent pattern match, shown here:

replicate:: Πn →a→Vec a n replicate Zero =Nil

replicate (Succ n’)x =x:>replicate n’ x

Let’s again consider the function bodies one at a time:

• Its type signature tells usNil has typeVec a ’Zero. Thus forNil to be well typed in replicate, we must know that n ∼ ’Zero. We indeed do know this, as we have scrutinized n and found thatn is ’Zero.

• For the recursive call to be well typed, we need to know that n ∼ ’Succ n’, which is, once again, what we know by the pattern match.

Note the difference between this case of dependent pattern match and the previous case of GADT pattern match. In GADT pattern matching, the equality assumption of interest is found by looking at the type of the constructor that we have found. In a dependent pattern match, on the other hand, the equality assumption of interest is between the scrutinee and the constructor. In our case here, the scrutinized value is not even of a GADT; Nat is a perfectly ordinary, Haskell98 datatype.

A question naturally comes up in this example: when should we do dependent pattern match and when should we do a traditional (non-dependent) pattern match? A naive answer might be to always do dependent pattern matching, as we can always feel free to ignore the extra, unused equality if we do not need it. However, this would not work in practice—with an equality assumption in scope, we cannot accurately infer the return type of a pattern match. Yet this last problem delivers us the solution: use dependent pattern matching only when we know a match’s result type, as propagated down via a bidirectional type system. (This is much the same way that today’s Haskell allows inference in the presence of higher-rank types [74]. See Section 6.4 for the 34If this last statement is a surprise to you, the introduction of Vytiniotis et al. [99] has a nice

details.) If we know a result type and do not need the dependent pattern match equality, no harm is done. On the other hand, if we do not know the result type, this design decision means that dependent pattern matching does not get in the way of inferring the types of Haskell98 programs.