Search code examples
haskellhaskell-lens

If pattern matching with `Maybe a` result


Following code illustrates my intention. I want to pattern match, if it doesn't result is Nothing, if matches result is Just something

data MyData = 
      A Int
    | B String
    | C


ifA (A i) = Just i 
ifA _     = Nothing

ifB (B str) = Just str
ifB _     = Nothing

ifC C = Just () 
ifC _ = Nothing 



mbMult3 i = Just (i*3)

concWorld str = Just (str ++ "World")

example1 v = ifA v >>= mbMult3

example2 v = ifB v >>= concWorld

-- example2 (B "Hello ,") == Just "Hello, World"
-- example2 (A 3) == Nothing

Are there other ways to do ifA ifB ifC.

Edit:

  1. I suspect that lens library might have something. But I know nothing about lens for now.
  2. Updated ifC

Solution

  • Prisms from the lens package model this. You can generate prisms for your type using makePrisms, then use ^? (or its prefix equivalent, preview) to access members of the sum type, producing Nothing if a different value is provided:

    {-# LANGUAGE TemplateHaskell #-}
    
    import Control.Lens
    
    data MyData
      = A Int
      | B String
      | C
      deriving (Show)
    
    makePrisms ''MyData
    
    ghci> A 42 ^? _A
    Just 42
    ghci> A 42 ^? _B
    Nothing
    ghci> C ^? _C
    Just ()
    

    The nice thing about prisms is that they compose with other optics (e.g. lenses, folds, and traversals), as well as use them to traverse through nested sum types by composing prisms:

    ghci> (A 42, C) ^? _1._A
    Just 42
    ghci> (B "hello", C) ^? _1._A
    Nothing
    ghci> Just (A 42) ^? _Just._A
    Just 42
    ghci> Just (B "hello") ^? _Just._A
    Nothing
    ghci> Nothing ^? _Just._A
    Nothing
    

    The lens package is fairly complicated, and explaining all of its features is well outside the scope of this answer. If you don’t need much of it, your solution is probably fine. But if you find yourself writing that sort of code a lot, it’s likely that lens can help, as long as you’re willing to accept the steep learning curve and often-confusing type errors.


    † More generally, ^? works on any Fold that produces zero or one value (or more, it just ignores all but the first one), but prisms are specifically designed with sum types in mind, so they are more relevant here.