Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Syntax for lazy pattern matching with `as` syntax

Tags:

haskell

ghci

In (vanilla) GHCi 8.6.5 following function was prefectly valid:

f xs@ ~(x:xt) = xs

If I now do the same in 9.0.1 I get an error

Suffix occurrence of @. For an as-pattern, remove the leading whitespace.

Just removing the white space between @ and ~ doesn't seem to suffice, as then @~ would be interpreted as an operator, so the only valid variation I found was

f xs@(~(x:xt)) = xs

I'd like to know the following, for which I couldn't find answers in the change notes:

  1. What exactly changed from 8.6.5 to 9.0.1 that introduced this incompatibility?
  2. Is xs@(~(x:xt)) really the best way to write this pattern, or is there a preferred way over this?
like image 265
flawr Avatar asked Jun 14 '21 14:06

flawr


People also ask

What is pattern matching in Haskell explain with example?

In a functional language, pattern matching involves checking an argument against different forms. A simple example involves recursively defined operations on lists. I will use OCaml to explain pattern matching since it's my functional language of choice, but the concepts are the same in F# and Haskell, AFAIK.

Where is pattern matching in Haskell?

We use pattern matching in Haskell to simplify our codes by identifying specific types of expression. We can also use if-else as an alternative to pattern matching. Pattern matching can also be seen as a kind of dynamic polymorphism where, based on the parameter list, different methods can be executed.

What is the difference between strict pattern match and lazy pattern match?

Generally, a lazy pattern match is translated to calling corresponding record field accessors. The key difference between strict pattern match is that the strict pattern match requires to check for the pair constructor before g can be evaluated.

Is there a way to enable lazy match in regex?

As far as I know, most regex engine is greedy by default. Add a question mark at the end of quantifier will enable lazy match. As @Andre S mentioned in comment. Greedy: Keep searching until condition is not satisfied.

What is the difference between greedy and lazy match in Python?

'Greedy' means match longest possible string. 'Lazy' means match shortest possible string. For example, the greedy h.+l matches 'hell' in 'hello' but the lazy h.+?l matches 'hel'. Brilliant, so lazy will stop as soon as the condition l is satisfied, but greedy means it will stop only once the condition l is not satisfied any more?

What is the difference between the lazy and greedy search methods?

If you want only the first match to be retrieved, use the search method instead. Greedy means it will consume your pattern until there are none of them left and it can look no further. Lazy will stop as soon as it will encounter the first pattern you requested.


2 Answers

The changes to the handling of ~ and @ in GHC 9.0 are described here. Quoting from the migration guide:

GHC 9.0 implements Proposal 229, which means that the !, ~, and @ characters are more sensitive to preceding and trailing whitespace than they were before. As a result, some things which used to parse one way will now parse differently (or throw a parse error).

like image 97
shree.pat18 Avatar answered Oct 08 '22 23:10

shree.pat18


Adding parentheses (variable@(~pattern)) is a good solution. Alternatively, you could use a let or where binding, or a separate lazy case:

  1. rehead :: a -> [a] -> [a]
    rehead x' xs0 = x' : xs
      where
        _x : xs = xs0
    
  2. rehead :: a -> [a] -> [a]
    rehead x' xs0 = let
     _x : xs = xs0
     in x' : xs
    
  3. {-# Language PatternGuards #-}
    
    rehead :: a -> [a] -> [a]
    rehead x' xs0
      | let _x : xs = xs0
      = x' : xs
    

    This can be very helpful if you want to use these bindings in subsequent guards.

  4. rehead :: a -> [a] -> [a]
    rehead x' xs0 = case xs0 of
      ~(_x : xs) -> x' : xs
    

All of these options are maximally lazy:

  • head (rehead 5 [1, 2])
  • = head (rehead 5 [])
  • = head (rehead 5 undefined)
  • = 5

If you’re using {-# Language Strict #-}, then you must write the let/where bindings as ~(_x : xs) = xs0 to allow [], and the list parameter binding as ~xs0 to allow undefined; to get an irrefutable pattern (not just lazy) with case, you must write ~(~(_x : xs)).

like image 3
Jon Purdy Avatar answered Oct 08 '22 23:10

Jon Purdy