如何从一元行动中提取价值

时间:2011-12-19 21:22:32

标签: haskell monads comonad

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

Hoogle告诉我们没有这样的功能。

你能解释一下原因吗?

8 个答案:

答案 0 :(得分:44)

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。

例如,Identity monad具有runIdentity :: Identity a -> a,并且几个monad具有相似的功能,但是无法一般地提供它。事实上,无法逃脱"来自monad对于像IO这样的monad来说是必不可少的。

答案 1 :(得分:22)

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

data Null a = Null

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

现在(Monad m) => m a -> a表示Null a -> a,即无中生有。你不能这样做。

答案 2 :(得分:14)

这不存在,因为Monad是组合的模式,而不是分解的模式。您可以随时将更多部分与其定义的界面放在一起。它没有说要分开任何东西。

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

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

fromJust的存在同样无关紧要。这不是Monad旨在描述的行为的一部分。其他人提供了许多类型的例子,其中extract没有价值。但是那些类型仍然支持Monad的预期语义。这个很重要。这意味着Monad是一个比你给予赞誉的更通用的界面。

答案 3 :(得分:8)

假设有这样一个功能:

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

现在你可以写一个像这样的“功能”:

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

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

或者用更简单的话说,如果有一个实际有用的extract操作,Haskell将不是纯粹的功能。

答案 4 :(得分:6)

  

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

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

  

Hoogle告诉我们没有这样的功能。你能解释一下原因吗?

这很简单,因为Hoogle在基础库中找不到与该类型签名匹配的任何功能!

更严重的是,我想你要求的是monadic解释。问题是安全意味着。 (另见my previous thoughts on magicMonadUnwrap :: Monad m => m a -> a

假设我告诉你我有一个类型为[Int]的值。由于我们知道[]是一个monad,这类似于告诉你我有一个类型为Monad m => m Int的值。因此,我们假设您希望从Int中获取[Int]。好吧,你想要哪Int?第一个?最后一个?如果我告诉你的价值实际上是一个空列表怎么办?在这种情况下,甚至没有Int给你!因此对于列表来说,尝试提取单个值就像不安全一样。即使它是安全的(非空列表),您也需要特定于列表的功能(例如,head)以通过希望f :: [Int] -> Int来阐明您的含义。希望你能从这里知道Monad m => m a -> a含义根本没有明确定义。对于同一个monad来说它可能有多种含义,或者对于某些monad来说它可能意味着什么都没有,有时候,它只是不安全。

答案 5 :(得分:5)

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

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

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

现在绝对没有合理的默认方式来从String中获取Parser String。实际上,除了Monad之外,根本无法获得一个String。

答案 6 :(得分:1)

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

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

为什么hoogle根本不列出此功能,这似乎是因为未对comonad程序包建立索引,否则我认为Monad约束将被警告,并且extract将出现在带有{ {1}}个实例。也许是因为hoogle解析器不完整,并且在某些代码行上失败了。

我的替代答案:

  1. 如果已导入类型的构造函数,则可以执行-可能是递归的案例分析
  2. 您可以使用Comonad作为替代代码结构,将使用提取的值的代码链接到monad,只要您可以将monad转换为“ IO()”以打印输出你完成了。这看起来不像提取,但数学与现实世界并不相同。

答案 7 :(得分:-1)

嗯,技术上IO monad有unsafePerformIO

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