haskellmonadsderivingderivingvia

defining new monad instance


A few days ago I was trying to prove monad laws by creating a new monad instance but I found myself stuck at defining the new monad instance.

{-# LANGUAGE DeriveFunctor, InstanceSigs #-}
import Control.Monad

newtype Test a = Test { getTest :: [Maybe a] }
  deriving Functor

instance Applicative Test where
  pure = return
  (<*>) = liftM2 ($)

instance Monad Test where
  return :: a -> Test a
  return a = Test $ [Just a]
  
  (>>=) :: Test a -> (a -> Test b) -> Test b
  g >>= f = concat (map f g)  --Tried to do something like this

I tried something following the list monad definition, but is stuck because concat expects [[a]] but here it gets [Test b], so maybe there are other functions available or is there a way to make concat work on the newType? Any suggestions are appreciated. Thanks.


Solution

  • Unlike type aliases, newtype wrappers need to be manually applied and removed. Replace g >>= f = concat (map f g) with Test g >>= f = Test $ concat (map (getTest . f) g).

    This will leave you with only one more type error: g has type [Maybe a] instead of the needed [a]. We can tack on a catMaybes (needs import Data.Maybe) to take care of that: Test g >>= f = Test $ concat (map (getTest . f) $ catMaybes g). Now it compiles.

    Unfortunately, this instance isn't lawful. I'll leave it as an exercise for the reader to establish why not, and whether it can be easily fixed.