There's a very common problem that is very easy to get into with haskell. Code-snippet that describes it is this:
data JobDescription = JobOne { n :: Int }
                    | JobTwo
                    | JobThree { n :: Int }
  deriving (Show, Eq)
taskOneWorker :: JobDescription -> IO ()
taskOneWorker t = do
    putStrLn $ "n: " ++ (show $ n t)
main :: IO ()
main = do
  -- this runs ok:
  taskOneWorker (JobOne 10)
  -- this fails at runtime:
  -- taskOneWorker JobTwo
  -- this works, but we didn't want it to:
  -- taskOneWorker (JobThree 10)
I described this problem and it's possible solutions in this article: https://www.fpcomplete.com/user/k_bx/playing-with-datakinds
Here, on StackOverflow, I'd like to ask you -- what are the best solutions to this problem, using which tools and what documentation?
I suggest using a Prism.  The Prism _JobOne represents the Int value that lives inside the JobOne constructor.  In taskOneWorker we look it up using ^?.  Either the value t is indeed a JobOne constructor and its argument is return in the Just, or it is not a JobOne constructor and we get back a Nothing.
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
data JobDescription = JobOne { n :: Int }
                    | JobTwo
                    | JobThree { n :: Int }
  deriving (Show, Eq)
$(makePrisms ''JobDescription)
taskOneWorker :: JobDescription -> IO ()
taskOneWorker t = do
  case t ^? _JobOne of
    Just n  -> putStrLn $ "n: " ++ show n
    Nothing -> putStrLn "Not a JobOne"
               -- Or 'return ()' if you want to ignore these cases
main :: IO ()
main = do
  taskOneWorker (JobOne 10)
  taskOneWorker JobTwo
  taskOneWorker (JobThree 10)
-- *Main> main
-- n: 10
-- Not a JobOne
-- Not a JobOne
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With