11
votes

I read an article which said:

Providing instances for the many standard type-classes [Functors] will immediately give you a lot of functionality for practically free

My question is: what is this functionality that you get for free (for functors or other type-classes)? I know what the definition of a functor is, but what do I get for free by defining something as a functor/other type-class. Something other than a prettier syntax. Ideally this would be general and useful functions that operate on functors/other type-classes.

My imagination (could be wrong) of what free means is functions of this sort: TypeClass x => useful x y = ..

== Edit/Additition ==

I guess I'm mainly asking about the more abstract (and brain boggling) type-classes, like the ones in this image. For less abstract classes like Ord, my object oriented intuition understands.

6

6 Answers

9
votes

Functors are simple and probably not the best example. Let's look at Monads instead:

  • liftM - if something is a Monad, it is also a Functor where liftM is fmap.
  • >=>, <=<: you can compose a -> m b functions for free where m is your monad.
  • foldM, mapM, filterM... you get a bunch of utility functions that generalize existing functions to use your monad.
  • when, guard* and unless -- you also get some control functions for free.
  • join -- this is actually fairly fundamental to the definition of a monad, but you don't need to define it in Haskell since you've defined >>=.
  • transformers -- ErrorT and stuff. You can bolt error handling onto your new type, for free (give or take)!

Basically, you get a wide variety of standard functions "lifted" to use your new type as soon as you make it a Monad instance. It also becomes trivial (but alas not automatic) to make it a Functor and Applicative as well.

However, these are all "symptoms" of a more general idea. You can write interesting, nontrivial code that applies to all monads. You might find some of the functions you wrote for your type--which are useful in your particular case, for whatever reason--can be generalized to all monads. Now you can suddenly take your function and use it on parsers, and lists, and maybes and...

* As Daniel Fischer helpfully pointed out, guard requires MonadPlus rather than Monad.

5
votes

Functors are not very interesting by themselves, but they are a necessary stepping stone to get into applicative functors and Traversables.

The main property which makes applicative functors useful is that you can use fmap with the applicative operator <*> to "lift" any function of any arity to work with applicative values. I.e. you can turn any a -> b -> c -> d into Applicative f => f a -> f b -> f c -> f d. You can also take a look at Data.Traversable and Data.Foldable which contain several general purpose functions that involve applicative functors.

Alternative is a specialized applicative functor which supports choice between alternatives that can "fail" (the exact meaning of "empty" depends in the applicative instance). Applicative parsers are one practical example where the definitions of some and many are very intuitive (e.g. match some pattern zero-or-more times or one-or-more times).

Monads are one of the most interesting and useful type-classes, but they are already well covered by the other answers.

Monoid is another type-class that is both simple and immediately useful. It basically defines a way to add two pieces of data together, which then gives you a generic concat as well as functionality in the aforementioned Foldable module and it also enables you to use the Writer monad with the data type.

4
votes

There are many of the standard functions in haskell that require that their arguments implement one or more type-classes. Doing so in your code allows other developers (or yourself) to use your data in ways they are already familiar with, without having to write additional functions.

As an example, implementing the Ord type-class will allow you to use things like sort, min, max, etc. Where otherwise, you would need sortBy and the like.

4
votes

Yes, it means that implementing the type class Foo gives you all the other functions that have a Foo constraint "for free".

The Functor type class isn't too interesting in that regard, as it doesn't give you a lot.

A better example is monads and the functions in the Control.Monad module. Once you've defined the two Monad functions (>>=) and return for your type, you get another thirty or so functions that can then be used on your type.

Some of the more useful ones include: mapM, sequence, forever, join, foldM, filterM, replicateM, when, unless and liftM. These show up all the time in Haskell code.

2
votes

As others have said, Functor itself doesn't actually get you much for free. Basically, the more high-level or general a typeclass is (meaning the more things fit that description), then the less "free" functionality you are going to get. So for example, Functor, and Monoid don't provide you with much, but Monad and Arrow provide you with a lot of useful functions for free.

In Haskell, it's still a good idea to write an instance for Functor and Monoid though (if your data type is indeed a functor or a monoid), because we almost always try to use the most general interface possible when writing functions. If you are writing a new function that can get away with only using fmap to operate on your data type, then there is no reason to artificially restrict that function to to Monads or Applicatives, since it might be useful later for other things.

1
votes

Your object-oriented intuition carries across, if you read "interface and implementation" for "typeclass and instance". If you make your new type C an instance of a standard typeclass B, then you get for free that your type will work with all existing code A that depends on B.

UML diagram

As others have said, when the typeclass is something like Monad, then the freebies are the many library functions like foldM and when.