Haskell 如何从一元行为中提取价值

Haskell 如何从一元行为中提取价值,haskell,monads,comonad,Haskell,Monads,Comonad,是否有带有签名的内置函数:(Monad m)=>ma->a Hoogle说没有这样的功能 你能解释一下原因吗?因为它可能没有意义(实际上,在许多情况下没有意义) 例如,我可以这样定义解析器Monad: data Parser a = Parser (String ->[(a, String)]) appendLine :: String -> String appendLine str = str ++ extract getLine 现在,从解析器字符串中获取字符串绝对没有合理

是否有带有签名的内置函数
:(Monad m)=>ma->a

Hoogle说没有这样的功能


你能解释一下原因吗?

因为它可能没有意义(实际上,在许多情况下没有意义)

例如,我可以这样定义解析器Monad:

data Parser a = Parser (String ->[(a, String)])
appendLine :: String -> String
appendLine str = str ++ extract getLine

现在,从
解析器字符串
中获取
字符串
绝对没有合理的默认方法。实际上,根本没有办法用单元格得到一个字符串。

也许有比这更好的答案,但是有一种方法可以看出为什么你不能有一个类型<代码>(Monad m)=m a->a < /> >考虑一个空单元格:

data Null a = Null

instance Monad Null where
    return a = Null
    ma >>= f = Null

现在
(Monad m)=>ma->a
意味着
空a->a
,即从无到有。你不能那样做。

单子只提供两个功能:

return :: Monad m => a -> m a
(>>=) :: Monad m => m a -> (a -> m b) -> m b
这两个函数都返回
ma
类型的函数,因此无法以任何方式组合它们以获得
Monad m=>ma->a
类型的函数。要做到这一点,您需要的不仅仅是这两个函数,因此您需要更多地了解
m
,而不是它是一个monad


例如,
Identity
monad具有
runIdentity::Identity a->a
,并且有几个monad具有类似的功能,但无法提供通用的功能。事实上,无法从单子中“逃逸”对于像
IO

这样的单子是至关重要的,因为
monad
是一种合成模式,而不是分解模式。您总是可以将更多的片段与它定义的接口放在一起。它没有说要拆开任何东西

询问为什么不能取出某些内容就像询问为什么Java的
Iterator
接口不包含向其迭代的内容添加元素的方法一样。这不是迭代器接口的功能

你关于特定类型有一种提取函数的论点也是这样的。
Iterator
的某些特定实现可能具有
add
函数。但是,由于它不是迭代器的作用,因此在某些特定实例上该方法的存在是不相关的

而fromJust的
的存在也同样无关紧要。它不是Monad所要描述的行为的一部分。其他人给出了许多类型的示例,其中没有
extract
可以使用的值。但是这些类型仍然支持
Monad
的预期语义。这很重要。这意味着
Monad
是一个比您认为的更通用的接口

是否有签名为
:(Monad m)=>ma->a的内置函数

如果Hoogle说没有……那么可能没有,假设您对“内置”的定义是“在基本库中”

Hoogle说没有这样的功能。你能解释一下原因吗

这很简单,因为Hoogle在基本库中没有找到任何与该类型签名匹配的函数

更严重的是,我想你是在要求一元解释。问题是安全和意义。(另见)


假设我告诉你我有一个类型为
[Int]
的值。因为我们知道
[]
是一个monad,这类似于告诉您我有一个类型为
monad m=>m Int
的值。因此,假设您想从
[Int]
中获取
Int
。那么,你想要哪个
Int
?第一个?最后一个?如果我告诉你的值实际上是一个空列表呢?在这种情况下,甚至没有一个
Int
给你!所以对于列表来说,像这样任意抽取一个值是不安全的。即使是在安全的情况下(非空列表),您也需要一个特定于列表的函数(例如,
head
)来阐明希望
f::[Int]>Int
的含义。希望您能从这里直觉地看出,
Monad m=>ma->a
的含义并没有很好地定义。它可能对同一个单子有多种含义,也可能对某些单子毫无意义,有时,它只是不安全。

假设有这样一个函数:

extract :: Monad m => m a -> a
现在您可以编写如下“函数”:

data Parser a = Parser (String ->[(a, String)])
appendLine :: String -> String
appendLine str = str ++ extract getLine
除非
extract
函数被保证永不终止,否则这将违反引用透明度,因为
附录行“foo”
的结果将(a)依赖于
“foo”
以外的内容,(b)在不同上下文中求值时,求值结果将不同

或者更简单地说,如果有一个真正有用的
提取操作,Haskell就不会是纯粹的功能性操作了。

好吧,从技术上讲,IO单子是有的


但是,正如名字本身所暗示的,这个函数是邪恶的,只有当你真正知道自己在做什么时才应该使用它(如果你必须问自己知道与否,那么你就不知道)

这里有一个有用的
提取
函数和一些其他与此相关的函数

它只为一些函子/单子定义,它不一定给出完整的答案,而是给出一个答案。因此,可能会有comonad的子类,它们为您提供了选择答案的中间阶段,您可以在其中控制答案。可能与Traversable的可能子类有关。我不知道这些东西是否在任何地方都有定义

hoogle根本没有列出这个函数的原因似乎是因为comonad包没有索引,否则我认为Monad约束会被警告,
extract
会出现在带有
comonad
实例的Monad的结果中。这可能是因为hoogle解析器不完整,并且在某些代码行中失败

我的备选答案是:

  • 您可以执行一个-可能重复