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

后端 未结 9 1186
不思量自难忘°
不思量自难忘° 2020-12-12 18:50

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

const x _ = x

I can\'t seem to find anything relevant regarding t

相关标签:
9条回答
  • 2020-12-12 19:07

    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

    0 讨论(0)
  • 2020-12-12 19:07

    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 讨论(0)
  • 2020-12-12 19:11

    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.

    0 讨论(0)
  • 2020-12-12 19:11

    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+).

    0 讨论(0)
  • 2020-12-12 19:15

    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.

    0 讨论(0)
  • 2020-12-12 19:23

    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.

    0 讨论(0)
提交回复
热议问题