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.
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.
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.
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