I'm doing exercises from the book "Programming in Haskell (2nd Edition)" and I have some problems in understanding the following:
"Given the following type of expressions
data Expr a = Var a | Val Int | Add (Expr a) (Expr a)
deriving Show
that contain variables of some type a, show how to make this type into instances of the Functor, Applicative and Monad classes. With the aid of an example, explain what the >>= operator for this type does."
I found a solution to the first question, which is the same as here: https://github.com/evturn/programming-in-haskell/blob/master/12-monads-and-more/12.05-exercises.hs (ex. 7), that is type correct.
The problem is that I cannot find out the sense of this exercise and the meaning of what this solution actually does.
To understand the solution, you need to get an intuition over a Functor, an Applicative and a Monad.
That being said fmap, <*> and >>= is just a way for one to be able to transform a data within an arbitrary F in your case that's an Expr from a -> b
Take a look at the Type class definitions of Functor, Applicative and Monad for example.
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
<*> :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
>>= :: m a -> (a -> m b) -> m b
Though on the bigger picture, these functions also execute effects of the algebraic datatype that have the type class instances for it.
For example, I will provide the rough definition of the Maybe monad.
instance Monad Maybe where
(Just something) >>= f = f something
Nothing >>= _ = Nothing
In this context, the bind or >>= combinator returns Nothing if there is Nothing for the input else it applies the arbitrary f on something such that f is a transformation from a -> Maybe b which satisfies the definition of the >>= combinator which is m a -> (a -> m b) -> m b where m is the Maybe datatype.
Related
I've got a type t that supports the following three operations:
extract :: t a -> a
duplicate :: t a -> t (t a)
(<*>) :: t (a -> b) -> t a -> t b
Naturally I can also write bind:
(>>=) :: f a -> (a -> f b) -> f b
(>>=) x f = f (extract x)
And join:
join :: t (t a) -> t a
join = extract
But I can't write fmap nor pure
So this is kind of a "monad", and kind of a "comonad", but without fmap.
Technically, I have got an fmap and pure, but they're constrained.
I looked at various constrained functor style packages, but they all seem to constrain (<*>) as well, but in my case (<*>) is not constrained.
Is there an existing typeclass I could squeeze this type into?
If anyone is interested, the actual type I'm dealing with is the Closure type
I am looking for a function with type similar to:
Monad m => (a, b) -> (b -> m c) -> m (a, c)
It appears to me as some combination of bind (>>=) and a lens operation.
I am aware that I can solve this with a pattern match after a bind, but my gut tells me there is a "simpler" way to write this by leveraging lenses.
Is there any such operation?
This is definitely lensy. The monad is actually just a bit of a distraction because all you need is a functor:
changesecond (a, b) f = fmap (a,) (f b)
I'm pretty sure the _2 lens can be made to do your bidding with a basic lens thing like maybe over but I'm not too familiar with the library yet.
Edit
No combinator is really needed. You can write
changesecond pair f = _2 f pair
You should be able to work this out from the general definition of the Lens type.
Edit 2
This simple example demonstrates the main theme of Van Laarhoven lens construction:
Extract the focus from the context.
Apply the given function to produce a functorful of results.
Use fmap to restore the contexts to the results.
Ed Kmett's lens library elaborates on this theme in various ways. Sometimes it strengthens the functor constraint. Sometimes it generalizes the function to a profunctor. In the case of Equality, it removes the functor constraint. It just turns out that the same basic type shape can express a lot of different ideas.
Your function is just forM = flip mapM, or for = flip traverse if you relax the Monad constraint to Applicative. The Functor being traversed is (,) a.
Prelude> let foo :: Applicative f => (a, b) -> (b -> f c) -> f (a, c); foo p k = traverse k p
Prelude> :t foo
foo :: Applicative f => (a, b) -> (b -> f c) -> f (a, c)
Prelude> foo (1,2) (\x -> [x,2*x])
[(1,2),(1,4)]
(Also, as dfeuer points out, you don't even need Applicative in this specific case.)
Functors can be covariant and contravariant. Can this covariant/contravariant duality also be applied to monads?
Something like:
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
class ContraMonad m where
return :: a -> m a
contrabind :: m a -> (b -> m a) -> m b
Does ContraMonad class make sense? Any examples?
Well, of course, it's possible to define it, but I doubt it would be of any use.
There is a popular saying that "monad is just a monoid in a category of endofunctors". What it means is, first of all, that we have a category of endofunctors (meaning, (covariant) functors from some category to itself), and what's more, we have some multiplication on this endofunctors (in this case — composition). And then monad fits into some general framework that we don't have to worry about right now. The point is, there is no "multiplication" of contravariant functors. Composition of two covariant functors is again a covariant functor; but composition of two contravariant functors is not a contravariant functor (rather it's a covariant functor, so, a totally different beast).
So, "contravariant monads" do not really make sense.
A contravariant functor is a functor from one category into its opposite category, i.e. from one category into another (albeit closely related) one. OTOH, a monad is foremostly an endofunctor i.e. from one category into itself. So it can't be contravariant.
This kind of stuff always tends to be a lot clearer when you consider the “fundamental mathematical” definition of monads:
class Functor m => Monad m where
pure :: a -> m a
join :: m (m a) -> m a
As you see there aren't really any arrows in there that you could turn around in the result, like you did with contrabind. Of course there is
class Functor n => Comonad n where
extract :: n a -> a
duplicate :: n a -> n (n a)
but comonads are still covariant functors.
Unlike monads, applicatives (monoidal functors) needn't be endofunctors, so I believe these can be turned around. Let's start from the “fundamental” definition:
class Functor f => Monoidal f where
pureUnit :: () -> f ()
fzipWith :: ((a,b)->c) -> (f a, f b)->f c -- I avoid currying to make it clear what the arrows are.
(exercise: define a derived Applicative instance in terms of this, and vice versa)
Turning it around
class Contravariant f => ContraApp f where
pureDisunit :: f () -> ()
fcontraunzip :: ((a,b)->c) -> f c->(f a, f b)
-- I'm not sure, maybe this should
-- be `f c -> Either (f a) (f b)` instead.
No idea how useful that would be. pureDisunit is certainly not useful, because its only implementation is always const ().
Let's try writing the obvious instance:
newtype Opp a b = Opp { getOpp :: b -> a }
instance Contravariant (Opp a) where
contramap f (Opp g) = Opp $ g . f
instance ContraApp (Opp a) where
pureDisunit = const ()
fcontraunzip z (Opp g)
= (Opp $ \a -> ???, Opp $ \b -> ???) -- `z` needs both `a` and `b`, can't get it!
I don't think this is useful, though you might be able to define it with something like clever knot-tying recursion.
What might be more interesting is a contravariant co-monoidal functor, but this gets too weird for me right now.
Brent Yorgey's Typeclassopedia gives the following exercise:
Give an example of a type of kind * -> * which cannot be made an
instance of Functor (without using undefined).
Please tell me what "cannot be made an instance of Functor" means.
Also, I'd appreciate an example, but perhaps as a spoiler so that you can, please, guide me to the answer.
Let's talk about variances.
Here's the basic notion. Consider the type A -> B. What I want you to imagine is that such a type is similar to "having a B" and also "owing an A". In fact, if you pay back your A you immediately receive your B. Functions are kind of like escrow in that way.
The notion of "having" and "owing" can extend to other types. For instance, the simplest container
newtype Box a = Box a
behaves like this: if you "have" a Box a then you also "have" an a. We consider types which have kind * -> * and "have" their argument to be (covariant) functors and we can instantiate them to Functor
instance Functor Box where fmap f (Box a) = Box (f a)
What happens if we consider the type of predicates over a type, like
newtype Pred a = Pred (a -> Bool)
in this case, if we "have" a Pred a, we actually "owe" an a. This arises from the a being on the left side of the (->) arrow. Where fmap of Functor is defined by passing the function into the container and applying it to all the places where we "have" our inner type, we can't do the same for Pred a since we don't "have" and as.
Instead, we'll do this
class Contravariant f where
contramap :: (a -> b) -> (f b -> f a)
Now that contramap is like a "flipped" fmap? It will allow us to apply the function to the places where we "own" a b in Pred b in order to receive a Pred a. We might call contramap "barter" because it encodes the idea that if you know how to get bs from as then you can turn a debt of bs into a debt of as.
Let's see how it works
instance Contravariant Pred where
contramap f (Pred p) = Pred (\a -> p (f a))
we just run our trade using f prior to passing it on into the predicate function. Wonderful!
So now we have covariant and contravariant types. Technically, these are known as covariant and contravariant "functors". I'll also state immediately that almost always a contravariant functor is not also covariant. This, thus, answers your question: there exist a bunch of contravariant functors which are not able to be instantiated to Functor. Pred is one of them.
There are tricky types which are both contravariant and covariant functors, though. In particular, the constant functors:
data Z a = Z -- phantom a!
instance Functor Z where fmap _ Z = Z
instance Contravariant Z where contramap _ Z = Z
In fact, you can essentially prove that anything which is both Contravariant and Functor has a phantom parameter.
isPhantom :: (Functor f, Contravariant f) => f a -> f b -- coerce?!
isPhantom = contramap (const ()) . fmap (const ()) -- not really...
On the other hand, what happens with a type like
-- from Data.Monoid
newtype Endo a = Endo (a -> a)
In Endo a we both owe and receive an a. Does that mean we're debt free? Well, no, it just means that Endo wants to be both covariant and contravariant and does not have a phantom parameter. The result: Endo is invariant and can instantiate neither Functor nor Contravariant.
A type t of kind * -> * can be made an instance of Functor if and only if it is possible to implement a law-abiding instance of the Functor class for it. So that means you have to implement the Functor class, and your fmap has to obey the Functor laws:
fmap id x == x
fmap f (fmap g x) == fmap (f . g) x
So basically, to solve this, you have to name some type of your choice and prove that there's no lawful implementation of fmap for it.
Let's start with a non-example, to set the tone. (->) :: * -> * -> * is the function type constructor, as seen in function types like String -> Int :: *. In Haskell, you can partially apply type constructors, so you can have types like (->) r :: * -> *. This type is a Functor:
instance Functor ((->) r) where
fmap f g = f . g
Intuitively, the Functor instance here allows you to apply f :: a -> b to the return value of a function g :: r -> a "before" (so to speak) you apply g to some x :: r. So for example, if this is the function that returns the length of its argument:
length :: [a] -> Int
...then this is the function that returns twice the length of its argument:
twiceTheLength :: [a] -> Int
twiceTheLength = fmap (*2) length
Useful fact: the Reader monad is just a newtype for (->):
newtype Reader r a = Reader { runReader :: r -> a }
instance Functor (Reader r) where
fmap f (Reader g) = Reader (f . g)
instance Applicative (Reader r) where
pure a = Reader (const a)
Reader f <*> Reader a = Reader $ \r -> f r (a r)
instance Monad (Reader r) where
return = pure
Reader f >>= g = Reader $ \r -> runReader g (f r) r
Now that we have that non-example out of the way, here's a type that can't be made into a Functor:
type Redaer a r = Redaer { runRedaer :: r -> a }
-- Not gonna work!
instance Functor (Redaer a) where
fmap f (Redaer g) = ...
Yep, all I did is spell the name backwards, and more importantly, flip the order of the type parameters. I'll let you try and figure out why this type can't be made an instance of Functor.
According to the Typeclassopedia (among other sources), Applicative logically belongs between Monad and Pointed (and thus Functor) in the type class hierarchy, so we would ideally have something like this if the Haskell prelude were written today:
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Pointed f where
pure :: a -> f a
class Pointed f => Applicative f where
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
-- either the traditional bind operation
(>>=) :: (m a) -> (a -> m b) -> m b
-- or the join operation, which together with fmap is enough
join :: m (m a) -> m a
-- or both with mutual default definitions
f >>= x = join ((fmap f) x)
join x = x >>= id
-- with return replaced by the inherited pure
-- ignoring fail for the purposes of discussion
(Where those default definitions were re-typed by me from the explanation at Wikipedia, errors being my own, but if there are errors it is at least in principle possible.)
As the libraries are currently defined, we have:
liftA :: (Applicative f) => (a -> b) -> f a -> f b
liftM :: (Monad m) => (a -> b) -> m a -> m b
and:
(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
ap :: (Monad m) => m (a -> b) -> m a -> m b
Note the similarity between these types within each pair.
My question is: are liftM (as distinct from liftA) and ap (as distinct from <*>), simply a result of the historical reality that Monad wasn't designed with Pointed and Applicative in mind? Or are they in some other behavioral way (potentially, for some legal Monad definitions) distinct from the versions that only require an Applicative context?
If they are distinct, could you provide a simple set of definitions (obeying the laws required of Monad, Applicative, Pointed, and Functor definitions described in the Typeclassopedia and elsewhere but not enforced by the type system) for which liftA and liftM behave differently?
Alternatively, if they are not distinct, could you prove their equivalence using those same laws as premises?
liftA, liftM, fmap, and . should all be the same function, and they must be if they satisfy the functor law:
fmap id = id
However, this is not checked by Haskell.
Now for Applicative. It's possible for ap and <*> to be distinct for some functors simply because there could be more than one implementation that satisfies the types and the laws. For example, List has more than one possible Applicative instance. You could declare an applicative as follows:
instance Applicative [] where
(f:fs) <*> (x:xs) = f x : fs <*> xs
_ <*> _ = []
pure = repeat
The ap function would still be defined as liftM2 id, which is the Applicative instance that comes for free with every Monad. But here you have an example of a type constructor having more than one Applicative instance, both of which satisfy the laws. But if your monads and your applicative functors disagree, it's considered good form to have different types for them. For example, the Applicative instance above does not agree with the monad for [], so you should really say newtype ZipList a = ZipList [a] and then make the new instance for ZipList instead of [].
They can differ, but they shouldn't.
They can differ because they can have different implementations: one is defined in an instance Applicative while the other is defined in an instance Monad. But if they indeed differ, then I'd say the programmer who wrote those instances wrote misleading code.
You are right: the functions exist as they do for historical reasons. People have strong ideas about how things should have been.