David Roundy <droundy@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