Search code examples
haskellpattern-matchingoption-type

Wrap a partial pattern matching into a Maybe


Is there any way to wrap a partial pattern-match into a Maybe?

I wish to write some code kind of like this:

fromMaybe defaultValue $ do
  [a, b] <- mkList :: [Int]
  [x] <- mkList' a b :: [Int]
  return $ x

where this expression's value would be defaultValue if mkList evaluates to a list with length /= 2, or if mkList' a b evaluates to a list of length /= 1. Otherwise the expression evaluates to the only element returned by mkList' a b.

I figured the Maybe monad would be great to achieve this. But how can I turn a failed pattern-matching into a Nothing, and a successful match into a Just?

Is it possible to achieve this in Haskell?


Solution

  • Your code almost works as is. You only need to add some pure and change the type:

    import Data.Maybe
    
    f defaultValue mkList mkList' = fromMaybe defaultValue $ do
      [a, b] <- pure mkList :: Maybe [Int]
      [x] <- pure (mkList' a b) :: Maybe [Int]
      return x
    

    You don't even need those :: Maybe [Int] annotations.

    In GHCi you can see it has the expected behavior:

    ghci> f 0 [1,2,3] (\x y -> [x,y])
    0
    ghci> f 0 [1,2] (\x y -> [x])
    1
    ghci> f 0 [1,2] (\x y -> [y])
    2
    ghci> f 0 [1,2] (\x y -> [x,y])
    0
    

    The mechanism behind it is explained by Daniel Wagner in the comments:

    do
     [a, b] <- <X>
     <Y>
    

    is syntax sugar for

    let ok [a, b] = do <Y>
        ok _ = fail "..."
    in <X> >>= ok
    

    (And for Maybe, the definition of fail is fail _ = Nothing.)

    See the Report.

    Note that this only works inside do blocks where the monad is an instance of MonadFail.