0
votes

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.

2

2 Answers

2
votes

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.

0
votes

They can be derived the the MaybeT [] transformer:

{-# Language DerivingVia              #-}
{-# Language StandaloneKindSignatures #-}

import Control.Applicative       (Alternative)
import Control.Monad             (MonadPlus)
import Control.Monad.Fix         (MonadFix)
import Control.Monad.Trans.Maybe (MaybeT(..))
import Control.Monad.Zip         (MonadZip)
import Data.Kind                 (Type)

type    Test :: Type -> Type
newtype Test a = Test { getTest :: [Maybe a] }
 deriving (Functor, Foldable, Applicative, Alternative, Monad, MonadFail, MonadFix, MonadPlus, MonadZip)
 via MaybeT []

:instances MaybeT []:

>> :instances MaybeT []
instance [safe] GHC.Base.Alternative (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] Applicative (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] Functor (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] Monad (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] GHC.Base.MonadPlus (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] Data.Functor.Classes.Eq1 (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] Foldable (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] Data.Functor.Classes.Ord1 (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] Data.Functor.Classes.Read1 (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] Data.Functor.Classes.Show1 (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] Traversable (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] MonadFail (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] Control.Monad.Fix.MonadFix (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] Control.Monad.Zip.MonadZip (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] Monad (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] Applicative (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’
instance [safe] Functor (MaybeT [])
  -- Defined in ‘Control.Monad.Trans.Maybe’

Some of these instances can also be derived via Compose [] Maybe but famously monads don't compose. :instances Compose [] Maybe:

>> :instances Compose [] Maybe
instance Alternative (Compose [] Maybe)
  -- Defined in ‘Data.Functor.Compose’
instance Applicative (Compose [] Maybe)
  -- Defined in ‘Data.Functor.Compose’
instance Functor (Compose [] Maybe)
  -- Defined in ‘Data.Functor.Compose’
instance Foldable (Compose [] Maybe)
  -- Defined in ‘Data.Functor.Compose’
instance Eq1 (Compose [] Maybe)
  -- Defined in ‘Data.Functor.Compose’
instance Ord1 (Compose [] Maybe)
  -- Defined in ‘Data.Functor.Compose’
instance Read1 (Compose [] Maybe)
  -- Defined in ‘Data.Functor.Compose’
instance Show1 (Compose [] Maybe)
  -- Defined in ‘Data.Functor.Compose’
instance Traversable (Compose [] Maybe)
  -- Defined in ‘Data.Functor.Compose’
instance Alt (Compose [] Maybe) -- Defined in ‘Data.Functor.Alt’
instance Hashable1 (Compose [] Maybe)
  -- Defined in ‘hashable-1.3.0.0:Data.Hashable.Class’