简体   繁体   English

如何从单调行动中获取价值

[英]How to extract value from monadic action

Is there a built-in function with signature :: (Monad m) => ma -> a ? 是否存在带有签名:: (Monad m) => ma -> a的内置函数?

Hoogle tells that there is no such function. Hoogle告诉我们没有这样的功能。

Can you explain why? 你能解释为什么吗?

A monad only supplies two functions: 一个monad仅提供两个功能:

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

Both of these return something of type ma , so there is no way to combine these in any way to get a function of type Monad m => ma -> a . 两者都返回类型为ma东西,因此无法以任何方式将它们组合起来以获得类型Monad m => ma -> a的函数Monad m => ma -> a To do that, you'll need more than these two functions, so you need to know more about m than that it's a monad. 为此,您需要的不仅仅是这两个函数,因此您需要了解更多有关m不是m

For example, the Identity monad has runIdentity :: Identity a -> a , and several monads have similar functions, but there is no way to provide it generically. 例如, Identity monad具有runIdentity :: Identity a -> a ,并且几个monad具有相似的功能,但是无法通用地提供它。 In fact, the inability to "escape" from the monad is essential for monads like IO . 实际上,对于像IO这样的单子来说,无法从单子中“逃脱”是至关重要的。

There is probably a better answer than this, but one way to see why you cannot have a type (Monad m) => ma -> a is to consider a null monad: 可能有比这更好的答案,但是查看为什么不能拥有类型(Monad m) => ma -> a是考虑空monad:

data Null a = Null

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

Now (Monad m) => ma -> a means Null a -> a , ie getting something out of nothing. 现在(Monad m) => ma -> a表示Null a -> a ,即从空中得到一些东西。 You can't do that. 你不能那样做。

This doesn't exist because Monad is a pattern for composition, not a pattern for decomposition. 这不存在,因为Monad是合成的模式,而不是分解的模式。 You can always put more pieces together with the interface it defines. 您始终可以将更多片段与其定义的界面放在一起。 It doesn't say a thing about taking anything apart. 它没有说要拆散任何东西。

Asking why you can't take something out is like asking why Java's Iterator interface doesn't contain a method for adding elements to what it's iterating over. 问为什么不能取出某些东西就像问为什么Java的Iterator接口不包含用于在其迭代的内容中添加元素的方法。 It's just not what the Iterator interface is for. 这不是Iterator接口的用途。

And your arguments about specific types having a kind of extract function follows in the exact same way. 关于具有提取函数类型的特定类型的论点也完全相同。 Some particular implementation of Iterator might have an add function. Iterator某些特定实现可能具有add功能。 But since it's not what Iterator s are for, the presence that method on some particular instance is irrelevant. 但是由于不是Iterator的目的,因此该方法在某些特定实例上的存在无关紧要。

And the presence of fromJust is just as irrelevant. fromJust的存在无关紧要。 It's not part of the behavior Monad is intended to describe. 这不是Monad打算描述的行为的一部分。 Others have given lots of examples of types where there is no value for extract to work on. 其他人给出了很多类型的示例,这些示例没有任何可extract价值。 But those types still support the intended semantics of Monad . 但是这些类型仍然支持Monad的预期语义。 This is important. 这个很重要。 It means that Monad is a more general interface than you are giving it credit for. 这意味着Monad是比您所称赞的更通用的界面。

Suppose there was such a function: 假设有这样一个功能:

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

Now you could write a "function" like this: 现在您可以编写这样的“函数”:

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

Unless the extract function was guaranteed never to terminate, this would violate referential transparency, because the result of appendLine "foo" would (a) depend on something other than "foo" , (b) evaluate to different values when evaluated in different contexts. 除非保证extract函数永远不会终止,否则这将违反参照透明性,因为appendLine "foo"的结果将(a)依赖于"foo"以外的东西,(b)在不同上下文中求值时会得出不同的值。

Or in simpler words, if there was an actually useful extract operation Haskell would not be purely functional. 或者用简单的话来说,如果有一个实际上有用的extract操作,Haskell将不能纯粹地发挥作用。

Is there a build-in function with signature :: (Monad m) => ma -> a ? 是否存在带有签名:: (Monad m) => ma -> a的内置函数?

If Hoogle says there isn't...then there probably isn't, assuming your definition of "built in" is "in the base libraries". 如果Hoogle说没有……那么可能就没有了,假设您对“内置”的定义是“在基础库中”。

Hoogle tells that there is no such function. Hoogle告诉我们没有这样的功能。 Can you explain why? 你能解释为什么吗?

That's easy, because Hoogle didn't find any function in the base libraries that matches that type signature! 这很容易,因为Hoogle在基库中找不到与该类型签名匹配的任何函数!

More seriously, I suppose you were asking for the monadic explanation. 更严重的是,我想您是在要求单子说明。 The issues are safety and meaning . 问题是安全意义 (See also my previous thoughts on magicMonadUnwrap :: Monad m => ma -> a ) (另请参阅我以前对magicMonadUnwrap :: Monad m => ma -> a想法magicMonadUnwrap :: Monad m => ma -> a

Suppose I tell you I have a value which has the type [Int] . 假设我告诉你我有一个[Int]类型的值。 Since we know that [] is a monad, this is similar to telling you I have a value which has the type Monad m => m Int . 既然我们知道[]是一个monad,这类似于告诉您我有一个类型为Monad m => m Int So let's suppose you want to get the Int out of that [Int] . 因此,假设您Int [Int]获取[Int] Well, which Int do you want? 好吧,您想要哪个Int The first one? 第一个? The last one? 最后一个? What if the value I told you about is actually an empty list? 如果我告诉您的值实际上是一个空列表怎么办? In that case, there isn't even an Int to give you! 在这种情况下,甚至没有一个Int可以给您! So for lists, it is unsafe to try and extract a single value willy-nilly like that. 因此,对于列表,尝试像这样随意地提取单个值是不安全的 Even when it is safe (a non-empty list), you need a list-specific function (for example, head ) to clarify what you mean by desiring f :: [Int] -> Int . 即使是安全的(非空列表),也需要特定于列表的函数(例如head )来通过f :: [Int] -> Int来阐明您的意思 Hopefully you can intuit from here that the meaning of Monad m => ma -> a is simply not well defined. 希望您可以从这里了解到Monad m => ma -> a含义并没有被很好地定义。 It could hold multiple meanings for the same monad, or it could mean absolutely nothing at all for some monads, and sometimes, it's just simply not safe. 它对于同一个monad可能具有多种含义,或者对于某些monad绝对没有任何意义,有时,这只是不安全的。

Because it may make no sense (actually, does make no sense in many instances). 因为它可能没有任何意义(实际上,在许多情况下确实没有任何意义)。

For example, I might define a Parser Monad like this: 例如,我可以这样定义一个解析器Monad:

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

Now there is absolutely no sensible default way to get a String out of a Parser String . 现在是绝对没有合理的默认方式来获得一个String出的Parser String Actually, there is no way at all to get a String out of this with just the Monad. 实际上,仅凭Monad根本无法从中获得String。

There is a useful extract function and some other functions related to this at http://hackage.haskell.org/package/comonad-5.0.4/docs/Control-Comonad.html http://hackage.haskell.org/package/comonad-5.0.4/docs/Control-Comonad.html上有一个有用的extract功能以及与此相关的其他一些功能。

It's only defined for some functors/monads and it doesn't necessarily give you the whole answer but rather gives an answer. 这只是对一些仿函数/单子定义,它并不一定给你全部答案,而是给出了一个答案。 Thus there will be possible subclasses of comonad that give you intermediate stages of picking the answer where you could control it. 因此,可能会有comonad的子类,这些子类为您提供了可以控制答案的中间阶段。 Probably related to the possible subclasses of Traversable. 可能与Traversable的可能子类有关。 I don't know if such things are defined anywhere. 我不知道是否在任何地方定义了这些东西。

Why hoogle doesn't list this function at all appears to be because the comonad package isn't indexed otherwise I think the Monad constraint would be warned and extract would be in the results for those Monads with a Comonad instance. 为什么hoogle根本不列出此功能,似乎是因为未对comonad程序包建立索引,否则我认为将警告Monad约束,并将extract出具有Comonad实例的Monad的结果。 Perhaps this is because the hoogle parser is incomplete and fails on some lines of code. 也许是因为hoogle解析器不完整,并且在某些代码行上失败了。

My alternative answers: 我的替代答案:

  1. you can perform a - possibly recursive - case analysis if you've imported the type's constructors 如果已导入类型的构造函数,则可以执行(可能是递归的)案例分析
  2. You can slink your code that would use the extracted values into the monad using monad >>= \\a -> return $ your code uses a here as an alternative code structure and as long as you can convert the monad to "IO ()" in a way that prints your outputs you're done. 您可以使用monad >>= \\a -> return $ your code uses a here将使用提取的值的代码链接到monad中,只要您可以将monad转换为“ IO()”, monad >>= \\a -> return $ your code uses a here作为替代代码结构。以打印输出的方式完成。 This doesn't look like extraction but maths isn't the same as the real world. 这看起来不像提取,但数学与现实世界并不相同。

Well, technicaly there is unsafePerformIO for the IO monad. 好吧,从技术上讲 ,IO monad有unsafePerformIO

But, as the name itself suggests, this function is evil and you should only use it if you really know what you are doing (and if you have to ask wether you know or not then you don't) 但是,顾名思义,此功能是有害的,只有在真正知道自己在做什么的情况下才应使用它(并且如果必须询问是否知道,那么就不知道)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM