[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.
Regards,
Malcolm
More information about the Haskell
mailing list