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