47

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

Hoogle 说没有这样的功能。

你能解释一下为什么吗?

4

8 回答 8

53

monad 只提供两个功能:

return :: Monad m => a -> m a
(>>=) :: Monad m => m a -> (a -> m b) -> m b

这两个都返回类型的东西m a,所以没有办法以任何方式组合它们来获得类型的函数Monad m => m a -> a。为此,您需要的不仅仅是这两个函数,因此您需要了解m的不仅仅是它是一个 monad。

例如,Identitymonad 有runIdentity :: Identity a -> a,并且几个 monad 有类似的功能,但是没有办法通用地提供它。事实上,无法从 monad 中“逃脱”对于像IO.

于 2011-12-19T21:31:46.157 回答
26

可能有比这更好的答案,但是要了解为什么不能拥有类型的一种(Monad m) => m a -> a方法是考虑 null monad:

data Null a = Null

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

现在的(Monad m) => m a -> a意思是Null a -> a,从无到有。你不能那样做。

于 2011-12-19T21:29:06.287 回答
18

这不存在,因为Monad它是一种组合模式,而不是一种分解模式。您总是可以将更多部分与它定义的接口放在一起。它并没有说要拆开任何东西。

问你为什么不能取出某些东西就像问为什么 Java 的Iterator接口不包含一个方法来添加元素到它正在迭代的东西。这不是Iterator界面的用途。

您关于具有某种提取功能的特定类型的论点以完全相同的方式遵循。的某些特定实现Iterator可能具有add功能。但是由于它不是Iterators 的用途,因此该方法在某些特定实例上的存在是无关紧要的。

并且存在fromJust同样无关紧要。Monad这不是要描述的行为的一部分。其他人给出了许多没有价值的类型示例extract。但是这些类型仍然支持Monad. 这个很重要。这意味着这Monad是一个比您认为的更通用的界面。

于 2011-12-19T21:56:30.670 回答
13

假设有这样一个函数:

extract :: Monad m => m a -> a

现在你可以写一个这样的“函数”:

appendLine :: String -> String
appendLine str = str ++ extract getLine

除非extract保证函数永远不会终止,否则这将违反引用透明性,因为appendLine "foo"(a) 的结果将取决于 以外的其他内容"foo",(b) 在不同的上下文中评估时评估为不同的值。

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

于 2011-12-20T00:19:14.207 回答
8

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

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

Hoogle 说没有这样的功能。你能解释一下为什么吗?

这很容易,因为 Hoogle 在基础库中没有找到任何与该类型签名匹配的函数!

更严重的是,我想你是在要求一元解释。问题是安全意义。(另见我之前的想法magicMonadUnwrap :: Monad m => m a -> a

假设我告诉你我有一个类型为 的值[Int]。由于我们知道这[]是一个 monad,这类似于告诉你我有一个类型为 的值Monad m => m Int。所以让我们假设你想Int摆脱它[Int]。嗯,Int你想要哪个?第一个?最后一个?如果我告诉你的值实际上是一个空列表怎么办?在那种情况下,甚至没有Int给你!因此,对于列表,尝试像这样随意提取单个值是不安全的。即使它是安全的(非空列表),您也需要一个特定于列表的函数(例如,head来阐明您希望. 希望你能从这里直觉f :: [Int] -> IntMonad m => m a -> a根本没有很好的定义。对于同一个 monad,它可能有多种含义,或者对于某些 monad 可能完全没有任何意义,有时,它只是不安全。

于 2011-12-19T23:02:06.023 回答
6

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

例如,我可能会像这样定义 Parser Monad:

data Parser a = Parser (String ->[(a, String)])

现在绝对没有明智的默认方法来String摆脱Parser String. 实际上,仅使用 Monad 根本无法从中获取字符串。

于 2011-12-19T21:27:35.877 回答
2

在http://hackage.haskell.org/package/comonad-5.0.4/docs/Control-Comonad.html有一个有用的extract功能和一些与此相关的其他功能

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

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

我的替代答案:

  1. 如果您已导入类型的构造函数,则可以执行 - 可能是递归的 - 案例分析
  2. 您可以将使用提取值的monad >>= \a -> return $ your code uses a here代码作为替代代码结构链接到 monad 中,只要您可以将 monad 转换为“IO ()”,以打印您完成的输出。这看起来不像提取,但数学与现实世界不同。
于 2018-08-24T07:04:47.347 回答
0

好吧,从技术上讲,IO monad 有unsafePerformIO

但是,正如名字本身所暗示的那样,这个功能是邪恶的,你应该只在你真的知道你在做什么的情况下使用它(如果你必须问你是否知道,那么你不知道)

于 2011-12-20T20:53:04.863 回答