Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this a Functor or Monad?

Tags:

haskell

I implemented my own Promise structure in C# and wanted to test the concept in Haskell so after some severe brain workouts (still very new to this) I produced

data Promise f a =  PendingPromise f | ResolvedPromise a | BrokenPromise deriving( Show )

class Future p where
        later :: (b -> c) -> p (a -> b) b -> p (a -> c) c
        resolve :: p (a -> b) a -> a -> p (a -> b) b

instance Future Promise where
        later g (PendingPromise f) = PendingPromise (g . f)
        later g (ResolvedPromise a) = ResolvedPromise (g a)
        resolve (PendingPromise f) a = ResolvedPromise (f a)

Figuring out how to write this datatype Promise f a was a real headache.

Anyway, the later method seems to be some sort of Applicative Functor and Promises are supposed to be Monads. Is it possible for me to make Promise an instance of some class and get this functionality instead of implementing my own class Future?


Edit Thanks to @bheklilr the later function was proven to be fmap in disguise with a little redefinition of the datatype

data Promise a b =  PendingPromise (a -> b) | ResolvedPromise b | BrokenPromise

instance Functor (Promise c) where
    fmap f (PendingPromise g) = PendingPromise (f . g)
    fmap f (ResolvedPromise a) = ResolvedPromise (f a)
    fmap f BrokenPromise = BrokenPromise

Knowing that (Promise a) is a Functor, it is easier to see why is a Monad.

like image 851
Cristian Garcia Avatar asked May 27 '14 17:05

Cristian Garcia


2 Answers

Yes, it is a monad. The easiest way to see this is observing Promise f a ≅ Maybe (Either f a), thus it's also isomorphic to the transformer equivalent which has an already-proven standard monad instance.

type Promise' f = ErrorT f Maybe

promise2Trafo :: Promise f a -> Promise' f a
promise2Trafo (PendingPromise f) = ErrorT . Just $ Left f
promise2Trafo (ResolvedPromise a) = ErrorT . Just $ Right a
promise2Trafo BrokenPromise = ErrorT Nothing

trafo2Promise :: Promise' f a -> Promise f a
trafo2Promise = ... -- straightforward inverse of `promise2Trafo`

instance Applicative Promise where
  pure = trafo2Promise . pure
  fp <*> xp = trafo2Promise $ promise2Trafo fp <*> promise2Trafo xp

and so on.

like image 187
leftaroundabout Avatar answered Oct 10 '22 20:10

leftaroundabout


What you'll want to do is try to implement the instance for Functor and check if it conforms to the Functor Laws. Start with the instance:

instance Functor (Promise f) where
    fmap f (PendingPromise g) = PendingPromise g
    fmap f (ResolvedPromise a) = ResolvedPromise (f a)
    fmap f BrokenPromise = BrokenPromise

Does this conform to the Functor Laws? Since the cases for PendingPromise and BrokenPromise are always the identity, I'll exclude them for brevity:

-- fmap id = id
fmap id (ResolvedPromise a) = ResolvedPromise (id a) = ResolvedPromise a

-- fmap (f . g) = fmap f . fmap g
fmap (f . g) (ResolvedPromise a) = ResolvedPromise (f (g a))
fmap f (fmap g (ResolvedPromise a)) = fmap f (ResolvedPromise (g a)) = ResolvedPromise (f (g a))

So yes, it does conform to the Functor laws.

Next, see if you can write the Applicative and Monad instances and prove that they conform to the respective laws. If you can write an instance that does, then your data type is a Monad, irrespective of the Future class.

like image 31
bheklilr Avatar answered Oct 10 '22 19:10

bheklilr