RussAbbott
RussAbbott

Reputation: 2738

How can I pattern match conditionally in a list comprehension in Haskell

I want to write something like this:

> [(i, j) | i <- [1..10], 
            Just j <- [if (even i) then Just (i `div` 2) else Nothing]]
[(2,1),(4,2),(6,3),(8,4),(10,5)]

Putting the condition in a list and using <- to extract the result seems ad hoc.

I tried:

> [(i, j) | i <- [1..10], 
            let Just j = if (even i) then Just (i `div` 2) else Nothing]

But that failed.

The following works but seems awkward:

> [(i, j) | i <- [1..10], 
            let x = if (even i) then Just (i `div` 2) else Nothing, 
            isJust x, 
            let Just j = x]
[(2,1),(4,2),(6,3),(8,4),(10,5)]

Is there a preferred way to do this.

I know this particular problem can be solved in other ways, but let's assume I want to pattern match conditionally in a list comprehension. Is there a good way to do that?

Thanks.

Upvotes: 4

Views: 812

Answers (1)

chi
chi

Reputation: 116139

Would this be acceptable for you?

[(i, j) | i <- [1..10], even i, let j = i `div` 2]

Another (IMO worse) option:

[(i, j) | i <- [1..10], 
          j <- if even i then [i `div` 2] else []]

Yet another:

do i <- [1..10]
   j <- guard (even i) >> return (i `div` 2)
   return (i,j)

Yet another:

[(i, j) | i <- [1..10], 
          j <- [i `div` 2 | even i]]

Really overkill and strongly not recommended:

{-# LANGUAGE PatternSynonyms, ViewPatterns #-}

testEvenRight :: Integral a => Either b a -> Maybe a
testEvenRight (Right n) | even n = Just n
testEvenRight _                  = Nothing

pattern EvenRight n <- (testEvenRight -> Just n)

list1 = [Right 2, Right 1, Left "abc", Right 4, Right 5]

list2 = [(i,j) | EvenRight i <- list1 , let j = i `div` 2]

Upvotes: 2

Related Questions