1

Below is some code coming from the answer to another stack overflow question. It's been several weeks i've started studying Haskell, and i had yet to encounter this particular syntax, and i can't find anywhere an explanation, not even a definition for it. So, the code:

data Pair a = P a a

instance Functor Pair where
  fmap f (P x y) = P (f x) (f y)

instance Monad Pair where
  return x = P x x
  P a b >>= f = P x y
     where P x _ = f a
           P _ y = f b

I spent the last half-hour trying to understand the meaning of it, or if something i already knew explained the definition of this bind (>>=) method. GHCI loaded it without fretting (well it demanded an Applicative instance but other than that), so it must be very much allowed, even if i have yet to understand the beginning of it.

What is the meaning of what looks like a definition by pattern-matching of an already defined data constructor? What are x and y, where do they come from?

Thanks to anyone who answers to that. And if anyone has a good idea for a good, specific title to this question -- given I really don't understand the very syntaxic meaning, I have trouble finding one.


@leftaroundabout gave me the information I needed, which was that that bit of code

P x y
  where P x _ = f a
        P _ y = f b

was a form of pattern-matching of the "value-unboxing" type, instead of the case of-like choice-oriented one. I was baffled by the presence of the _ pattern, and thus I didn't see the above two pattern-matchings as they were, that is, as the definitions of x then y, because it was done in a way I had never witnessed before.

I knew we could write something like that:

f :: foo -> (Int, Int)
...
i = let (a,b) = f x
    in a + b

but I didn't know we could use, in these cases of "value-unboxing" (here a and b for example, or x and y in the code that bugged me), the full extent of the possibilities in pattern-matching, that is, at least, definitions using _ to isolate the parts we don't want, the values we don't want to bind to any "label".

In short I didn't know that in the above example, this equation

P x _ = f a

was actually the definition of x through pattern-matching on the result of (f a), thus that it was strictly equivalent in effect to

x = g (f a)
  where g (P t _) = t

I was stuck at thinking it was the definition of the already-defined data constructor P.

Community
  • 1
  • 1
icelake
  • 13
  • 3

2 Answers2

4

There is no “particular syntax” here, just a normal pattern matching. The code is equivalent to

pFst, pSnd :: Pair a -> a
pFst (Pair x _) = x
pSnd (Pair _ y) = y

instance Monad Pair where
  return x = P x x
  P a b >>= f = P x y
     where x = pFst $ f a
           y = pSnd $ f b

If you inline pFst and pSnd, you see that this leads straight to the original definition.

If you're not convinced, consider that (non-recursive) where bindings can be replaced with lambda abstractions:

  P a b >>= f = (\(Pair x _) (Pair _ y) -> P x y)
                  (f a)      (f b)

Evidently, that lambda could just as well be written as a named local function:

  P a b >>= f = pFstAndPSnd (f a) (f b)
   where pFstAndPSnd (Pair x _) (Pair _ y) = P x y

Perhaps it would have been less confusing if you had seen the code as it would look for ordinary tuples:

(>>=) :: (c,c) -> (c -> (d,d)) -> (d,d)
(a,b) >>= f = (x,y)
  where (x,_) = f a
        (_,y) = f b

This is obviously not redefining the (,) constructor in any way, just using it to pattern-match on tuple results of a function.


Well, ok, perhaps it's not that obvious, seeing as you can also do stuff like let 1+2=4 in 1+2, and get 4 as the result.

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • Well, if I translate it correctly, it means that, apparently, one can pattern-match in reverse, defining parts of a pattern out of variables that, well are of the same pattern, so I could apparently write: `let (a,_) = (1,2); (_,b) = (3,4) in (a,b) == (1,4)`. This ability to define values by successive "partial" pattern-matching, taking bits from other variables, is something i merely ignored. I knew of it without `_`, basically, except of course when defining functions, but implicitly it's completely different here, don't you think? – icelake May 01 '16 at 19:31
  • By the way, you wrote `pFst (Pair _ y) = y` but were victim of the copy-paste hex, as it should have been `pSnd`. And thanks, of course. I tried to edit my previous comment but it got refused. – icelake May 01 '16 at 19:40
  • I'm sorta new at SOverflow, do I have to do something special now it got resolved? – icelake May 01 '16 at 19:42
  • Comments aren't really meant to be persistent, editable entities. Just delete a comment if it's not correct anymore. – leftaroundabout May 01 '16 at 19:51
  • No I meant to edit to add what I wrote in the second comment. Your footnote example is intriguing, must I understand that `1 + 2` is treated as a non-evaluated Integral (because of laziness), and since it's pattern-matched to be the "singleton" 4, then it's "blindly" evaluated as such? and recognized after the `in` of the `let in` probably because of the strategy of graph reduction of Haskell? – icelake May 01 '16 at 19:56
  • `let 1+2=4 in 1+2` defines a _new local infix function `+`_ (shadowing `Prelude.+`) which is only defined for the particular arguments `1` and `2`. Then evaluating this function as `1+2` happens to work, whereas `1+3` will give an error. You can shadow any value in Haskell (like `x` or `(+)`), but not constructors. – leftaroundabout May 01 '16 at 20:00
  • I see, of course it was the function `(+)` that was defined! ^^ Yeah, well it'd be hard with constructors. Though, beyond the scope of one file, the statement `import ... hiding` I suppose could be said to allow some pseudo-shadowing of constructors, don't you think? (I insist on the **pseudo**) Thanks again! – icelake May 01 '16 at 20:34
  • Yes, but it always requires a `data` or `newtype` keyword to define a new constructor (shadowing or otherwise). – leftaroundabout May 01 '16 at 20:36
2

well P is indeed the constructor for the Pair so a and b will be the first and second element of this (a bit strange) pair (if you say x = P 1 2 an do x >>= f then here a = 1 and b = 2 - it's exactly pattern-matching

My guess is that you have trouble with the definition of the function. If it helps you could write it as:

(>>=) (P a b) f = P x y
    where ...

too

see: just as you can us the operator between arguments so can you define it there

Random Dev
  • 51,810
  • 9
  • 92
  • 119
  • It wasn't exactly this part that which I didn't understand, but the part that you ellipse-d, aka the part where `P x y` is defined through "reversed" pattern-matching. I mean that of course in my terminology; aka the one that is supposed to fill the left side by taking things from the right side like a mirror (like in do-notation Monad "decapsulation", vaguely speaking), instead of defining the right side in terms of the left side of the `=` sign, as for function definitions, and `case of` pattern-matching, in which case of course it's an `->` instead of an `=`. – icelake May 01 '16 at 19:51
  • it's not *defined* - it's just what *pattern-matching* is - while defining the function `(>>=)` you pattern-match the first argument to `P a b` and since `Pair`only has this one constructor this will catch everything (so no need to have another one) - you should have seen this from the way you work with lists in function (where you have usually one pattern/case `f [] = ...` and one `f (x:xs) = ...` - again one for each constructor of a list – Random Dev May 01 '16 at 20:13
  • of course you can do this with a `case ... of` construct too - but it reads much nicer this way don't you agree? – Random Dev May 01 '16 at 20:14
  • you're somewhat deaf, but I'll repeat again: I got **no** problem with `P a b`. I had a problem with the pattern-matching-based definitions of `x` and `y`, well, with the global area after the `=` of the bind method definition, not before. I'm well aware of function definition pattern matching. Do you really believe that after several weeks of learning, anyone could miss that essential point in haskell? As you said, I (anyone) **should** already have understood what you try repetitively to make me understand, and in fact, well I **have** indeed. – icelake May 01 '16 at 20:27
  • @trigone Hey, polite! This is your question we're answering. – leftaroundabout May 01 '16 at 20:33
  • hindsight is 20/20, and I agree, I shouldn't have reacted as such, I'm sorry. It's just I had the feeling Carsten hadn't really read much either of my question nor of my first answer-comment. still doesn't excuse my behavior. do I remove my abrasive comment to replace it with a nicer one? – icelake May 01 '16 at 20:41
  • 1
    @trigone no worries, I don't suppose Carsten is too offended and you won't be expelled for one impolite comment. More as general advice for the future: if you find something off the point on StackOverflow, just ignore it – the poster may be targeting an aspect of the question that you never intended, but this can still be useful for _other_ users who find the post in the future and were in fact looking for that other aspect. – leftaroundabout May 01 '16 at 21:13
  • well as soon as I'm offended by the internet or this site I'll just stop using it - btw @trigone I read and tried to understand your questions but your *terminology* just does not make any sense to me (reversed pattern-matching for example - isn't this just construction?) so please don't be offended as well when I tell you to please grab yourself some book or tutorial and start with the very basics (that might go for manners as well) – Random Dev May 02 '16 at 04:03