What is the 'Const' applicative functor useful for?
-
21-06-2021 - |
Question
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?
Solution
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.
OTHER TIPS
It's useful when you have a function or data structure that works for all (Applicative
) Functor
s, 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.
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
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.