30

I've just found Const in the documentation of Control.Applicative, but I have a hard time working out where this is useful, over just using Monoid directly.

What am I missing?

ocharles
  • 6,172
  • 2
  • 35
  • 46

4 Answers4

28

It's rather useful when combined with Traversable.

getConst . traverse Const :: (Monoid a, Traversable f) => f a -> a

That's the general recipe for glomming a bunch of stuff together. It was one of the use cases which convinced me that it was worth separating Applicative from Monad. I needed stuff like generalized elem

elem :: Eq x => x -> Term x -> Bool

to do occur-checking for a Traversable Term parametrized by the representation of free variables. I kept changing the representation of Term and I was fed up modifying a zillion traversal functions, some of which were doing accumulations, rather than effectful mapping. I was glad to find an abstraction which covered both.

pigworker
  • 43,025
  • 18
  • 121
  • 214
  • http://www.soi.city.ac.uk/~ross/papers/Applicative.html section 4 is a good reference for the traverse trick – Daniel Jul 18 '12 at 18:08
  • Doesn't `foldMap id` produce the same result with less constraints (`Foldable` instead of `Traversable`)? – Petr Aug 27 '12 at 07:01
  • 1
    Indeed, that's why `Foldable` exists. However, it's rather useful that everything `Traversable` is `Foldable`, with the above being the construction of `foldMapDefault`. Plug: SHE supports `DefaultSuperclassInstances`, so that everything `Traversable` is by silent default made `Foldable` in that way. – pigworker Sep 06 '12 at 08:56
  • Which can also be written: `(^. traverse)`. Obvious to many, I'm sure, but perhaps useful to others, like me, who don't always find some of these expressions easy to read. – Paul Gardiner May 23 '21 at 13:01
7

It's useful when you have a function or data structure that works for all (Applicative) Functors, and wish to reuse it in a degenerate sense. It's analogous to passing in const or id to functions that work given arbitrary functions.

Van Laarhoven lenses are defined in terms of arbitrary Functors, and use Const to derive a field accessor (and the equally trivial Identity to derive a field updater).

Traversable types, as pigworker mentions, are another example of this.

dave4420
  • 46,404
  • 6
  • 118
  • 152
7

As dave4420 mentions, implementing accessor and updater for Van Laarhoven lenses involves Const functor. To elaborate:

{-# LANGUAGE Rank2Types #-}

import Control.Applicative
import Control.Monad.Identity

-- The definition of Van Laarhoven lenses:
type Lens a b = forall f . Functor f => (b -> f b) -> (a -> f a)

-- Getter passes the Const functor to the lens:
get :: Lens a b -> a -> b
get l = getConst . (l Const)

-- Updater passes the Identity functor to the lens:
modify :: Lens a b -> (b -> b) -> (a -> a)
modify l f = runIdentity . l (Identity . f)

set :: Lens a b -> b -> (a -> a)
set l r = modify l (const r)

-- Example: -------------------------------------------

data Person = Person { _name :: String, _age :: Int }
  deriving Show

name :: Lens Person String
name f (Person n a) = fmap (\x -> Person x a) (f n)

age :: Lens Person Int
age f (Person n a) = fmap (\x -> Person n x) (f a)

main :: IO ()
main = do
    let john = Person "John" 34
    print $ get age john
    print $ set name "Pete" john
Petr
  • 62,528
  • 13
  • 153
  • 317
2

As Gabriel Gonzalez points out, Const is used to provide Getters to lenses. http://hackage.haskell.org/package/lens-tutorial-1.0.0/docs/Control-Lens-Tutorial.html. There you only use that Const is a functor.

For traversals, you need the full Applicative behaviour, and it becomes what pigworker described in another answer.

Julian Birch
  • 2,605
  • 1
  • 21
  • 36