开发者

What's the point of 'const' in the Haskell Prelude?

开发者 https://www.devze.com 2023-04-04 18:18 出处:网络
Looking through the Haskell Prelude, I see a function const: const x _ = x I can\'t seem to find anything relevant regarding this function.

Looking through the Haskell Prelude, I see a function const:

const x _ = x

I can't seem to find anything relevant regarding this function.

What's the point? Can anyone give an example of where this function m开发者_如何学Goight be used?


It's useful for passing to higher-order functions when you don't need all their flexibility. For example, the monadic sequence operator >> can be defined in terms of the monadic bind operator as

x >> y = x >>= const y

It's somewhat neater than using a lambda

x >> y = x >>= \_ -> y

and you can even use it point-free

(>>) = (. const) . (>>=)

although I don't particularly recommend that in this case.


To add to hammar's excellent direct answer: humble functions like const and id are really useful as a higher order function for the same reason that they are fundamental in the SKI combinator calculus.

Not that I think haskell's prelude functions were modeled consciously after that formal system or anything. It's just that creating rich abstractions in haskell is very easy, so you often see these types of theoretical things emerge as practically useful.

Shameless plug, but I blogged about how the Applicative instance for (->) are actually the S and K combinators here, if that's the kind of thing you're into.


I can't seem to find anything relevant regarding this function.

Many of the other answers discuss relatively esoteric (at least to the newcomer) applications of const. Here is a simple one: you can use const to get rid of a lambda that takes two arguments, throws away the first one but does something interesting with the second one.

For instance, the following (inefficient but instructive) implementation of length,

length' = foldr (\_ acc -> 1 + acc) 0

can be rewritten as

length' = foldr (const (1+)) 0

which is perhaps more elegant.

The expression const (1+) is indeed semantically equivalent to \_ acc -> 1 + acc, because it takes one argument, throws it away, and returns the section (1+).


A simple example for using const is Data.Functor.(<$). With this function you can say: I have here a functor with something boring in it, but instead I want to have that other interesting thing in it, without changing the shape of the functor. E.g.

import Data.Functor

42 <$ Just "boring"
--> Just 42

42 <$ Nothing
--> Nothing

"cool" <$ ["nonsense","stupid","uninteresting"]
--> ["cool","cool","cool"]

The definition is:

(<$) :: a -> f b -> f a
(<$) =  fmap . const

or written not as pointless:

cool <$ uncool =  fmap (const cool) uncool

You see how const is used here to "forget" about the input.


Another use is to implement class member functions that have a dummy argument which should not be evaluated (used to resolve ambiguous types). Example that could be in Data.bits:

instance Bits Int where
  isSigned = const True
  bitSize  = const wordSize
  ...

By using const we explicitly say that we are defining constant values.

Personally I dislike the use of dummy parameters, but if they are used in a class then this is a rather nice way of writing instances.


Say you want to create a list of Nothings equal to the length of a string. As const returns its first argument, no matter the second, you can do:

listOfNothings :: String -> [Maybe Char]
listOfNothings = (map . const) Nothing

or, more explicitly:

listOfNothing st = map (const Nothing) st


const may be just the implementation you are looking for in conjunction with other functions. Here is an example I discovered.

Say we want to rewrite a structure of 2-tuples to another structure of 2-tuples. I might express this as so:

((a,b),(c,d)) ⇒ (a,(c,(5,a)))

I can give a straight-forward definition with pattern matching:

f ((a,b),(c,d)) = (a,(c,(5,a)))

What if I want a pointless (tacit) solution for these kind of rewrites? Some thinking and fiddling later, the answer is that we can express any rewrites with (&&&), const, (.), fst, snd. Note that (&&&) is from Control.Arrow.

The solution of the example using these functions is:

(fst.fst &&& (fst.snd &&& (const 5 &&& fst.fst)))

Note the similarity with (a,(c,(5,a))). What if we replace &&& with ,? Then it reads:

(fst.fst, (fst.snd, (const 5, fst.fst)))

Notice how a is the first element of the first element, and that is what fst.fst projects. Notice how c is the first element of the second element, and that is what fst.snd projects. That is, variables become the path to their source.

const allows us to introduce constants. Interesting how the name lines up with the meaning!

I then generalised this idea with Applicative so that you can write any function in a pointless style (so long as you have case analysis available as functions, such as maybe, either, bool). Again, const plays the role of introducing constants. You can see this work in the Data.Function.Tacit package.

When you start abstractly, at the goal, and then work towards an implementation, you can be surprised by the answers. That is to say, any one function may be as mysterious as any one cog in a machine. If you pull back to bring the whole machine into view, however, you can understand the context in which that cog is necessary.


Say you want to rotate a list. This is an idiomatic way to do so in Haskell:

rotate :: Int -> [a] -> [a] rotate _ [] = [] rotate n xs = zipWith const (drop n (cycle xs)) xs

This function zips two arrays with the function const, the first being an infinite cyclic array, the second being the array you started with.

const acts as the bounds check, and uses the original array to terminate the cyclic array.

See: Rotate a list in Haskell


I can't seem to find anything relevant regarding this function.

Suppose you would like to generate all subsequences of a given list.

For each list element, at a given point you have a choice of True (include it in the current subsequence) or False (do not include it). This can be done using the filterM function.

Like this:

 λ> import Control.Monad
 λ> :t filterM
 filterM :: Applicative m => (a -> m Bool) -> [a] -> m [a]
 λ> 

For example, we want all the subsequences of [1..4].

 λ> filterM  (const [True, False])  [1..4]
 [[1,2,3,4],[1,2,3],[1,2,4],[1,2],[1,3,4],[1,3],[1,4],[1],[2,3,4],[2,3],[2,4],[2],[3,4],[3],[4],[]]
 λ> 
0

精彩评论

暂无评论...
验证码 换一张
取 消