[Haskell] pattern matching on record fields and position

Malcolm Wallace Malcolm.Wallace at cs.york.ac.uk
Wed Nov 2 10:00:18 EST 2005

David Roundy <droundy at darcs.net> writes:

> I have a couple of related (almost conjugate) proposals/questions.
> Basically, I've been thinking about how to make code more robust with
> respect to changes in the data types.

This sounds a bit like "views", proposals for which have been around
for years, but never adopted.  There is a related language feature
(extension) called "pattern guards" which /is/ implemented in ghc,
and gives most of the power of views.

> I'd like to be able export a data type with constructors in such a way that
> positional pattern matching isn't possible--but field-based pattern
> matching *is* possible.  One could just use a coding policy, but I like the
> compiler enforcing things like this for me.  Perhaps there's already a
> trick to do this?

So, you could export just the field names, but not the constructors.
Instead of patterns, use pattern guards.

> I would like users (who import this module) to be able to write
>     case fps of { PS { my_start = s } -> print s }

This would become
      case fps of { _ | s <- my_start fps   -> print s }

It slightly abuses the pattern guard notation, because the pattern
is a degenerate one - just a variable name - so it always succeeds.
Thus, for a type with more than one constructor, like this:

>     data Foo = AB { a :: String, b :: Int } | B { b :: Int }

the similar construct
      case foo of { _ | x <- a foo   -> print x
                      | otherwise    -> putStrLn "error" }
would never reach the otherwise clause, even when given a B constructor.
Instead, it would crash the program.

One common style people use today to enable the later extension of a datatype
is empty-record patterns:

      case foo of { A{} -> print (a foo)
                  ; B{} -> putStrLn "error" }

but as you no doubt have immediately realised, this forces the
constructors to be visible, and therefore does not prevent the
programmer from using explicit positional patterns.  It is just a
convention, not enforceable.

> The second feature I'd like (and even better if it's something that already
> exists, although I've been told that it isn't) would be to be able to have
> record field names that are exported so as to not allow them to be used as
> accessor functions if those functions might lead to failure.  For example:
> data Foo = AB { a :: String, b :: Int } | B { b :: Int }
> I would like "a" to be useable for pattern matching, but not as the
> function "a :: Foo -> String", which is dangerous, in that it really ought
> (in my opinion) to have the type Foo -> Maybe String.

Probably you really want "extensible" records, with all the rho-typing
trickery that makes it possible to decide statically whether a
particular field exists when an accessor is applied to the record.
There are several competing proposals for this - the OOHaskell one
requires no extensions to Haskell'98.


More information about the Haskell mailing list