Haskell前奏中的“ const”有什么意义?


92

浏览Haskell前奏,我看到一个函数 const

const x _ = x

我似乎找不到与此功能相关的任何内容。

重点是什么?谁能举例说明该功能的使用位置?


10
举个例子:backgroundColor :: Text -> Color对我来说backgroundColor = const White

Answers:


83

当您不需要所有灵活性时,将其传递给高阶函数很有用。例如,>>可以根据单子绑定算子将单子序列算符定义为

x >> y = x >>= const y

比使用lambda更加整洁

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

你甚至可以免费使用它

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

尽管在这种情况下,我不建议这样做。


9
+1。使用解析器组合器时,它也经常出现。
Fred Foo

47
嗯,这更像是一个“函数生成器”-我将其与一个参数一起使用,它为我提供了一个函数(带有一个参数),该函数始终返回常数值。因此map (const 42) [1..5]结果[42, 42, 42, 42, 42]
stusmith 2011年

2
斯图史密斯:你明白了。const在将一个参数应用于需要一个函数(例如传递给map)的函数时很有用。
Conal

8
@stusmith:您可以通过一些有趣的方式使用它:head = foldr const (error "Prelude.head: empty list")

27

要补充hammar的一个很好的直接答案:像这样的谦虚函数constid作为高阶函数真正有用,原因是它们在SKI组合器演算中基础的。

我并不是认为haskell的前奏功能是在该正式系统或其他任何东西之后有意识地建模的。仅仅是在haskell中创建丰富的抽象非常容易,因此您经常会看到这些类型的理论事物在实践中很有用。

无耻的插头,但我的博客上讲述了应用型实例如何(->)实际上是SK组合子在这里,如果这是你到之类的话。


8
好吧,SKI组合器无疑影响了Prelude。我记得与Joe Fasel争论是否应该包括S组合器。
2011

4
顺便说一句,((->) e)也是阅读器monad- Reader以及类似的东西只是newtype包装器-然后ask函数是id,所以这也是I组合器。如果你看一下而不是在哈斯克尔库里的原BCKW基础BK以及Wfmapreturn,和join分别。
CA McCann

1
答案中的博客链接已死。现在应该指向的位置:brandon.si/code/...
nsxt

22

一个简单的使用示例constData.Functor.(<$)。使用此功能,您可以说:我的函子里面有些无聊,但我想在其中包含其他有趣的东西,而无需更改函子的形状。例如

import Data.Functor

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

42 <$ Nothing
--> Nothing

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

定义是:

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

或写得并非毫无意义:

cool <$ uncool =  fmap (const cool) uncool

const将在此处看到如何“忘记”输入内容。


21

我似乎找不到与此功能相关的任何内容。

许多其他答案都讨论了的相对深奥的应用(至少对于新手而言)const。这是一个简单的例子:您可以const用来摆脱带有两个参数的lambda,抛弃第一个参数,但对第二个参数做一些有趣的事情。

例如,以下(效率低下但有启发性的)实现 length

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

可以改写成

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

这也许更优雅。

该表达式const (1+)确实在语义上等效于\_ acc -> 1 + acc,因为它接受一个参数,将其扔掉,然后返回section (1+)


4
我花了5分钟的时间来理解它的工作原理:)
Mukesh Soni

15

另一个用途是实现具有伪参数的类成员函数,该伪参数不应进行评估(用于解析歧义类型)。可能在Data.bits中的示例:

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

通过使用const,我们明确表示正在定义常量值。

我个人不喜欢使用伪参数,但是如果在类中使用伪参数,那么这是编写实例的一种不错的方法。


代理参数确实要好得多,而且当针对最近的GHC时,类型应用程序可以巧妙地解决问题。
dfeuer

2

const可能只是您要与其他功能结合使用的实现。这是我发现的一个例子。

假设我们想将2元组的结构重写为2元组的另一种结构。我可以这样表达:

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

我可以通过模式匹配给出一个简单的定义:

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

如果我想要针对此类重写的无意义(默认)解决方案怎么办?经过一番思考和摆弄,答案是我们可以用表示任何重写(&&&), const, (.), fst, snd。注意(&&&)来自Control.Arrow

使用这些功能的示例的解决方案是:

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

注意与的相似性(a,(c,(5,a)))。如果我们替换&&&,?然后显示为:

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

注意a第一个元素的第一个元素如何,这就是fst.fst项目。请注意c,第二个元素的第一个元素如何,那就是fst.snd项目。也就是说,变量成为其来源的路径。

const允许我们引入常量。有趣的是名称与含义如何对齐!

然后我概括这个想法与应用型,让您可以在一个毫无意义的风格编写的任何函数(只要您有可用的功能,如案例分析maybeeitherbool)。再次,const起到引入常量的作用。您可以在Data.Function.Tacit中看到此工作包中。

当您抽象地开始目标时,然后为实现而努力时,答案可能会让您感到惊讶。也就是说,任何一种功能都可能像机器中任何一个齿轮一样神秘。但是,如果撤回查看整个机器,则可以了解需要该齿轮的环境。


2

假设您要创建一个Nothings等于字符串长度的列表。当const返回其第一个参数时,无论第二个参数如何,您都可以执行以下操作:

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

或者,更明确地说:

listOfNothing st = map (const Nothing) st

0

假设您要旋转列表。在Haskell中,这是一种惯用的方法:

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

此函数使用函数压缩两个数组const,第一个是无限循环数组,第二个是您开始使用的数组。

const 充当边界检查,并使用原始数组终止循环数组。

看到: 在Haskell中旋转列表


0

我似乎找不到与此功能相关的任何内容。

假设您要生成给定列表的所有子序列。

对于每个列表元素,在给定的点上,您可以选择True(在当前子序列中包括它)或False(不包括它)。这可以使用filterM函数来完成。

像这样:

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

例如,我们需要的所有子序列[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],[]]
 λ> 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.