[Haskell-cafe] Re: [Haskell] View patterns in GHC: Request for
feedback
Conor McBride
ctm at cs.nott.ac.uk
Wed Jul 25 08:07:07 EDT 2007
Hi
Ok, I'm not quite under my rock yet,,,
On 25 Jul 2007, at 10:28, apfelmus wrote:
> Jules Bean wrote:
>> Have you tried using pattern guards for views?
>>
>> f s | y :< ys <- viewl s = ....
>> | EmptyL <- viewl s = ....
This is annoying because the intermediate computation gets repeated. I
don't think view patterns fix this even if they sometimes hide it. I
worry
that the good behaviour of this feature depends on a compiler
noticing that
a repetition can be shared: this design makes the sharing harder to
express.
Of course,
> Hm, I'd simply use a plain old case-expression here
>
> f s = case viewl s of
> y :< ys -> ...
> EmptyL -> ...
which is fine if you're ready to commit to the right-hand side at
this point,
but what if the result of the intermediate computation tells you what
patterns to look for next?
Epigram's "with"-notation was never implemented in Epigram, but it
has been
implemented in Agda 2. At the moment, we'd write
f s with viewl s -- adds a new column to the left
f s | y :< ys = ... -- so you can now match whatever you like
f s | EmptyL = ...
Inside a "with-block", the patterns left of | must refine the original
patterns left of the "with". When you're fed up looking at the new
information, you can exit the block, drop the column and carry on as
normal
(see zip, below).
I'd hope that we might ultimately be able to elide the repeated lefts
if they are unchanged, but that's not implemented at the moment.
f s with viewl s -- adds a new column to the left
| y :< ys = ...
| EmptyL = ...
So zip might become
zip xs ys with viewl xs
| x :< xt with viewl ys
| y :< yt = (x, y) <| zip xt yt
zip _ _ = empty
or even
zip xs ys with viewl xs with viewl ys
| x :< xt | y :< yt = (x, y) <| zip xt yt
zip _ _ = empty
For more entertainment, a padding zip
pzip x' xs y' ys with viewl xs with viewl ys
| x :< xt | y :< yt = (x, y) <| pzip x
xt y yt
| x :< xt | EmptyL = fmap (flip (,) y') xs
| EmptyL | y :< yt = fmap ((,) x') ys
pzip _ _ _ _ = empty
Pattern matching is much weirder in a dependently typed setting, but
also a
lot more powerful. Inspecting the value of a pattern variable or of an
intermediate computation can leave us knowing more about (and hence
instantiate) other pattern variables. We get stuff like this
gcd :: Positive -> Positive -> Positive
gcd x y with trichotomy x y
gcd x (x + y) | LT x y = gcd x y
gcd x x | EQ x = x
gcd (y + x) y | GT x y = gcd x y
Here, matching on the result of trichotomy (with constructor
patterns, see?)
causes the original argument patterns to become more informative
(with non-linear non-constructor forms which are *guaranteed* to match
at no run time cost).
For us, it's a bad idea to try to think of analysing one scrutinee
independently of the other data we have to hand. We're naturally pushed
towards thinking about the left-hand side as a whole, to which
information
can be added, supporting further refinement. This is part of what James
McKinna and I were on about in "The view from the left", and it's
happening
on real computers now.
To sum up, one-shot matching on intermediate computations, as provided
by pattern guards or view-patterns, is conspicuously nasty in a
dependently
typed language, but for reasons which make it sometimes merely
annoying in
Haskell.
I'm disinclined to argue thus: "don't do your thing (which probably will
happen) because it messes up my thing (which probably won't, at least in
Haskell)". I'll just chuck these observations in the air and see what
happens.
All the best
Conor
PS Please may I have pattern synonyms anyway? They're cheap and they
serve a
different purpose. Maybe I should say more about how their absence is
seriously nasty for the way I work, another time.
More information about the Haskell-Cafe
mailing list