I'd love to see a copy of this go up on hackage for experimentation. Would you care to upload your code, or send it to me so I can upload it?
On Sat, Dec 20, 2008 at 9:34 PM, Jacques Carette <carette@mcmaster.ca> wrote:Example 14 also uses (.->.) where it should use (.>.), and it either
> Andrew Wagner wrote:
>>
>> Wadler posted a blog entry the other day about a paper on pattern-matching
>> in Haskell (http://wadler.blogspot.com/). I've taken a first stab at turning
>> it into actual code for hackage (http://hpaste.org/13215). There are two
>> commented-out definitions that don't type-check, though, and the types are
>> too wild for me to grok. Anybody have any suggestions for 1.) How to fix it
>> and/or 2.) How to use data/type/newtype to simplify the types and make it
>> more manageable? Thanks!
>
> Both errors are because you are using "any" instead of "any'"; you might
> wish to put
> import Prelude hiding any
> at the top of your code, just to avoid such confusion.
needs some more parentheses or some precedence declarations for the
operators.
One oddity in the paper is the type of the failure continuations, ()
> To make the types more readable (but not necessarily more manageable), I
> have made some changes to my version of this code.
-> ans. I'm guessing that's left over from an earlier phase of
development. In my own transcription of the library, I eliminated the
() parameter without apparent loss of functionality.
I think I've managed to work out the structure of the types, which can
mostly be expressed in modern Haskell.
The matching part of the patterns have this general form:
type PMatch vec vec' ans = (vec -> ans) -> (() -> ans) -> vec' -> ans
where vec and vec' are the list of argument types before and after the
subpattern match, and ans is the final answer. (In my code, I just use
ans instead of () -> ans for the failure continuation.)
This gets us:
nil :: PMatch vec vec ans
one :: a -> PMatch (a,vec) vec ans
(#) :: PMatch vec vec' ans -> PMatch vec' vec'' ans -> PMatch vec vec'' ans
fail :: PMatch vec vec' ans
catch :: PMatch vec vec' ans -> PMatch vec vec' ans -> PMatch vec vec' ans
These types are less general than the ones Haskell would infer, but
they do not appear to lose any functionality.
The currying part of the pattern is less easy to analyze. I've been
able to use type families to relate the curried and uncurried form of
the function types, but I'm working with GHC 6.8, so it's possible
this won't work with the more modern implementations.
Given the list of argument types and the answer type, generate a
curried function type:
type family Curry vec ans
type instance Curry () ans = ans
type instance Curry (a,vec) ans = a -> Curry vec ans
zero, succ zero, and so forth take a function in curried form and
transform it into a function that takes a nested tuple:
type CurryDigit vec ans = Curry vec ans -> vec -> ans
zero :: CurryDigit () ans
succ zero :: CurryDigit (a,()) ans
succ :: CurryDigit vec ans -> CurryDigit (a,vec) ans
succ . succ :: CurryDigit vec ans -> CurryDigit (a,(b,vec)) ans
So the currying part of the pattern will have the form:
type PCurry vec vec' ans = CurryDigit vec' ans -> CurryDigit vec ans
So a pattern has the type,
type Pattern a vec vec' ans = (PCurry vec vec' ans, a -> PMatch
vec vec' ans)
where a is the value being examined, vec and vec' are the list of
unbound argument types before and after the match, and ans is the
result.
var :: Pattern a (a,vec) vec ans
cst :: (Eq a) => a -> Pattern a vec vec ans
pair :: Pattern a vec vec' ans -> Pattern b vec' vec'' ans ->
Pattern (a,b) vec vec'' ans
Coming from the other side, match takes a value and a case statement
and produces a result:
type Case a ans = a -> (() -> ans) -> ans -- or just a -> ans ->
ans in my code
match :: a -> Case a ans -> ans
(|||) combines case statements:
(|||) :: Case a ans -> Case a ans -> Case a ans
and (->>) creates them from a pattern and a curried function,
(->>) :: Pattern a vec () ans -> Curry vec ans -> Case a ans
Note that (->>) requires the pattern to leave no unbound variables
after matching.
Given the way everything is polymorphic in ans, it may be possible to
hide it, but I haven't tried yet.
You can define the patterns manually:
> The principal weakness of these pattern-matching combinators is that there
> is no support for algebraic types, i.e. things like
> data Tree a = Leaf | Branch (Tree a) (Tree a)
> I can see how to use Typeable to deal with that, but is there a simpler way?
leaf = (id, \v -> case v of { Leaf -> nil; _ -> fail })
branch p q = (curry_p . curry_q, \v -> case v of { Branch l r ->
match_p l # match_q r; _ -> fail})
where
(curry_p, match_p) = p
(curry_q, match_q) = q
I assume generating these would be pretty straightforward to automate
with Template Haskell.
--
Dave Menendez <dave@zednenem.com>
<http://www.eyrie.org/~zednenem/>