Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Applicative functors other than monads and ZipList?

Two well-known examples of applicatives are monads and ziplists. Are there any other examples?

like image 761
sdcvvc Avatar asked Jan 22 '10 21:01

sdcvvc


People also ask

Is every applicative a monad?

An applicative is a data type that implements the Applicative typeclass. A monad is a data type that implements the Monad typeclass. A Maybe implements all three, so it is a functor, an applicative, and a monad.

Are all monads applicative functors?

Every Monad is an Applicative Just as IO , every monad can be made into an applicative functor.

Is applicative a functor?

Applicative functors are the programming equivalent of lax monoidal functors with tensorial strength in category theory. Applicative functors were introduced in 2008 by Conor McBride and Ross Paterson in their paper Applicative programming with effects.

Is maybe a functor or monad?

The Maybe sum type is a useful data type that forms a functor. Like many other useful functors, it also forms a monad. It can be useful when querying another data structure (like a list or a tree) for a value that may or may not be present.


2 Answers

From Time flies like an applicative functor by Conor McBride:

Structure cops will note that De is another example of an applicative functor which is not a monad — join would bring things from the far future to the near future, and that had better not be possible. However, where applicative functors in general only pull through traversable functors (containers with finitely many elements), De pulls through all containers. So it’s a bit special. I wonder what it is.

and

The De functor represents a fixed delay, rather than an arbitrary one. I’m dividing time into discrete slices. De x is the type of an x due at the next slice. De (De x) is thus the type of an x due in two slices’ time, and you can’t make it turn up any sooner!

Read the whole post. To answer the immediate question, the author’s conclusion is

Don’t Look!

OK, here’s the implementation. It’s a con.

newtype De x = De x deriving Show -- ssh, don't tell!

instance Functor De where
  fmap f (De x) = De (f x)

instance Applicative De where
  pure = De
  De f <*> De s = De (f s)

fix :: (De x -> x) -> x
fix f = f (De (fix f))
like image 144
Greg Bacon Avatar answered Sep 28 '22 16:09

Greg Bacon


I recently defined an Applicative instance for a newtype on top of (,,,), a "quad". (The standard library defines an instance for (,), but not (,,,). That's OK, as the standard implementation has different semantics than what I was after.)

The background is; I am parsing some old data, and the date format in the data is ambiguous. Each date in the data can be parsed into four possibilities, stored in the quad. I then want to validate each date in the quad to eliminate semantically invalid dates. (There are no months with 32 days, there is no month 34, there is no 5th quarter, etc.) Finally, I want to take each date in the dataset, and reduce the entire set to a quad representing which date formats are valid for the entire set. Then, I choose the best format out of those options, and assume that's what the date format of the dataset is.

This entire operation is very easy to express as applicative operations on the quad structure.

Here's the basic shape of the code:

My new type:

newtype DQ a = DQ (a, a, a, a) -- date quad
             deriving ...

instance Functor DQ where
   g `fmap` f = pure g <*> f

instance Applicative DQ where
   pure x  = DQ (x, x, x, x)
   DQ (g, h, i, j) <*> DQ (a, b, c, d) = DQ (g a, h b, i c, j d)

Some prerequisite "pure" functions:

parseDateInt :: Int -> DQ Date
validateDate :: Date -> Bool
extractBestDate :: DQ Date -> DQ Bool -> Date

So once we have the quad of parsed dates (from parseDateInt), we need to validate them:

validateDates :: DQ Date -> DQ Bool
validateDates = (validateDate <$>)

(This is only a Functor so far, but you could also write (pure validateDate <*>).

It's also worth noting the symmetry between validation of a single element, and validating each element of the set -- to validate one, you might write validateDate $ date; to validate a set, you write validateDate <$> dates. This is why fmap is written as <$>, it's function application to a functor.)

The step after this is to take a set of valid parses and fold that into a final result:

intuitDateType :: [DQ Bool] -> DQ Bool
intuitDateType dates = foldl1 (liftA2 (&&)) dates

So now you can go from the [Int] in the data file to a DQ Bool representing the possibly-valid date representations for a dataset. (And from there, associate each data point with a real date object, instead of the flaky Int that was supplied.)

So anyway, this post has gotten a bit long, but the idea is that an Applicative instance allowed me to solve my problem in about 3 lines of code. My problem domain was repeatedly applying functions to data in a container, which is what an Applicative functor does. There is no join operation on this data, so a Monad instance would not make much sense.

like image 24
jrockway Avatar answered Sep 28 '22 16:09

jrockway