Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does a comma in the guard syntax do?

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.)

like image 487
typetetris Avatar asked Oct 01 '17 06:10

typetetris


People also ask

How do guards work in Haskell?

A guard is basically a boolean expression. If it evaluates to True, then the corresponding function body is used. If it evaluates to False, checking drops through to the next guard and so on. If we call this function with 24.3, it will first check if that's smaller than or equal to 18.5.

Where is Haskell syntax?

where clause in the Haskell programming is bound to syntactic construct. but he use of where clause or function in Haskell we can remove the repetitive code as well, instead, we can hold the value into some variable and used it later.

What does a period do in Haskell?

In general terms, where f and g are functions, (f . g) x means the same as f (g x). In other words, the period is used to take the result from the function on the right, feed it as a parameter to the function on the left, and return a new function that represents this computation."

What is otherwise in Haskell?

It's not equivalent to _ , it's equivalent to any other identifier. That is if an identifier is used as a pattern in Haskell, the pattern always matches and the matched value is bound to that identifier (unlike _ where it also always matches, but the matched value is discarded).


2 Answers

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.

like image 50
Chris Martin Avatar answered Sep 22 '22 00:09

Chris Martin


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...)

like image 36
MathematicalOrchid Avatar answered Sep 25 '22 00:09

MathematicalOrchid