32

In a code base I'm reading, I found a function declaration like this (some parts are missing):

filepathNormalise :: BS.ByteString -> BS.ByteString
filepathNormalise xs
    | isWindows, Just (a,xs) <- BS.uncons xs, sep a, Just (b,_) <- BS.uncons xs, sep b
    = '/' `BS.cons` f xs

What does the comma do here?

(Only as a bonus, if someone readily knows this: is this syntax mentioned in Haskell Programming from first principles, and if so, where? As I can't remember reading about it.)

Will Ness
  • 70,110
  • 9
  • 98
  • 181
typetetris
  • 4,586
  • 16
  • 31
  • 3
    I would guess it is equivalen to logical and? But I could imagine, other haskell beginners will have the same question and therefore I thought, it migth be good, to raise this as question on stackoverflow. – typetetris Oct 01 '17 at 06:46
  • even if you knew the answer it'd be good to post this question (possibly together with an answer to it, if that were the case). – Will Ness Oct 01 '17 at 10:34
  • updated [the wiki](https://wiki.haskell.org/Keywords#.2C). – Will Ness Oct 01 '17 at 11:25

2 Answers2

29

Guards are described in Haskell 2010 section 3.13, Case Expressions (that section is about case expressions, not top-level declarations, but presumably the semantics are the same):

guards  →  | guard1, …, guardn      (n ≥ 1)
guardpat <- infixexp         (pattern guard)
        |  let decls               (local declaration)
        |  infixexp                (boolean guard)

For each guarded expression, the comma-separated guards are tried sequentially from left to right. If all of them succeed, then the corresponding expression is evaluated in the environment extended with the bindings introduced by the guards. That is, the bindings that are introduced by a guard (either by using a let clause or a pattern guard) are in scope in the following guards and the corresponding expression. If any of the guards fail, then this guarded expression fails and the next guarded expression is tried.

In the simple case, the comma serves a role similar to Boolean and. But the comma is more powerful in that each guard can introduce new bindings that are used by the subsequent guards (proceeding from left to right).

Commas in guards are uncommon enough (in my experience, at least) that I'd describe this feature as Haskell trivia -- not at all necessary to writing (or, for the most part, reading) Haskell. I suspect that Haskell Programming from first principles omits it for that reason.

Chris Martin
  • 30,334
  • 10
  • 78
  • 137
  • 3
    You quoted the definition but seem to have missed the important part: "**the bindings that are introduced by a guard ... are in scope in the following guards**". This is of course impossible if you replace all commas with `&&`s. For example, compare `case () of {() | x <- 1, x == 1 -> True}` with `case () of {() | x <- 1 && x == 1 -> True}`. The code asked about in the original question even includes a use of this feature. – amalloy Oct 01 '17 at 07:33
  • 1
    @amalloy Indeed. This feature is not redundant when pattern guards `p <- e` or lets `let x = e` are involved. If a guard only uses boolean expressions, then commas can indeed by replaced by `&&`. I'd say idiomatic Haskell uses `&&` if possible. – chi Oct 01 '17 at 07:50
  • Ah thank you, yes I was quite careless. Will make the appropriate corrections. – Chris Martin Oct 01 '17 at 08:03
  • 1
    I’d say commas in guards are very common. I use them all the time. They can be avoided if you use view patterns, but I’m not fond of those. – augustss Oct 02 '17 at 15:31
2

This syntax is not legal in Haskell '98; this was added to the language specification in Haskell 2010. It's part of the "pattern guards" language extension.

https://prime.haskell.org/wiki/PatternGuards

The real usefulness in this is in allowing you to pattern match inside a guard clause. The syntactic change also has the side-effect of allowing you to AND together several Boolean terms using commas.

(I personally really dislike this extension, and I'm a little shocked it made it into the official spec, but there we are...)

MathematicalOrchid
  • 61,854
  • 19
  • 123
  • 220