gpt4 book ai didi

haskell - 如何从不纯方法中返回纯值

转载 作者:行者123 更新时间:2023-12-02 02:51:33 25 4
gpt4 key购买 nike

我知道它听起来很琐碎,但是我想知道如何从函子中解开一个值并将其作为纯值返回?

我努力了:

f::IO a->a
f x=(x>>=)

f= >>=

我应该在右侧放置什么?我无法使用 return,因为它将再次将其重新包装。

最佳答案

这是一个经常被问到的问题:我如何不仅从Haskell,而且从其他语言中,从我的monad中提取“the”值。我有一个理论来解释为什么这个问题不断出现,因此我将尝试根据这个问题回答。希望对您有所帮助。

单一值的容器

您可以将函子(因此也称为monad)视为值的容器。使用(冗余)Identity函子最明显:

Prelude Control.Monad.Identity> Identity 42
Identity 42

这不过是一个值的包装器,在本例中为 42。对于此特定容器,您可以提取值,因为可以保证存在该值:
Prelude Control.Monad.Identity> runIdentity $ Identity 42
42

尽管 Identity似乎毫无用处,但您可以找到其他似乎包装单个值的函子。例如,在F#中,您经常会遇到诸如 Async<'a>Lazy<'a>之类的容器,这些容器用于表示异步或惰性计算(Haskell不需要后者,因为默认情况下是惰性的)。

在Haskell中,您还会发现许多其他单值容器,例如 SumProductLastFirstMaxMin等。所有这些通用的地方是它们包装了一个值,这意味着您可以提取值。

我认为,当人们第一次遇到函子和monad时,他们倾向于以这种方式想到数据容器的概念:作为单一值的容器。

可选值的容器

不幸的是,Haskell中一些常见的单子(monad)似乎支持该想法。例如, Maybe也是一个数据容器,但其中一个可以包含零或一个值。不幸的是,如果存在,您仍然可以提取该值:
Prelude Data.Maybe> fromJust $ Just 42
42

这样做的问题是 fromJust并非总计,因此如果您使用 Nothing值调用它,则会崩溃:
Prelude Data.Maybe> fromJust Nothing
*** Exception: Maybe.fromJust: Nothing

您可以使用 Either看到同样的问题。尽管我不知道提取 Right值的内置部分函数,​​但是您可以轻松地编写一个与模式匹配的函数(如果忽略编译器警告):
extractRight :: Either l r -> r
extractRight (Right x) = x

同样,它可以在“幸福之路”的情况下工作,但也很容易崩溃:
Prelude> extractRight $ Right 42
42
Prelude> extractRight $ Left "foo"
*** Exception: <interactive>:12:1-26: Non-exhaustive patterns in function extractRight

不过,由于存在像 fromJust这样的函数,因此我想它会欺骗函子和monad概念的新手,将其视为可以从中提取值的数据容器。

然后,当您第一次遇到 IO Int之类的东西时,我就能理解为什么您很想将它视为一个单一值的容器。从某种意义上说是这样,但从另一个意义上说不是。

多个值的容器

即使有了列表,您也可以(尝试)从列表中提取“the”值:
Prelude> head [42..1337]
42

不过,它可能会失败:
Prelude> head []
*** Exception: Prelude.head: empty list

但是,在这一点上,应该很清楚,试图从任意函子中提取“the”值都是胡说八道。列表是一个函子,但它包含任意数量的值,包括零和无限多个。

但是,您始终可以做的是编写将“包含”值作为输入并返回另一个值作为输出的函数。这是此类函数的任意示例:
countAndMultiply :: Foldable t => (t a, Int) -> Int
countAndMultiply (xs, factor) = length xs * factor

虽然您不能从列表中“提取值”,但可以将函数应用于列表中的每个值:
Prelude> fmap countAndMultiply [("foo", 2), ("bar", 3), ("corge", 2)]
[6,9,10]

由于 IO是函子,因此您也可以使用它来做同样的事情:
Prelude> foo = return ("foo", 2) :: IO (String, Int)
Prelude> :t foo
foo :: IO (String, Int)
Prelude> fmap countAndMultiply foo
6

关键是您不会从仿函数 中提取值,而是进入仿函数

单子(monad)

有时,您应用于函子的函数会返回已包装在同一数据容器中的值。例如,您可能具有将字符串分割为特定字符的功能。为了简单起见,让我们看一下内置函数 words,它将字符串分割成单词:
Prelude> words "foo bar"
["foo","bar"]

如果您有一个字符串列表,并将 words应用于每个字符串,则将获得一个嵌套列表:
Prelude> fmap words ["foo bar", "baz qux"]
[["foo","bar"],["baz","qux"]]

结果是一个嵌套的数据容器,在这种情况下为列表列表。您可以使用 join将其展平:
Prelude Control.Monad> join $ fmap words ["foo bar", "baz qux"]
["foo","bar","baz","qux"]

这是monad的原始定义:它是可以平整的函子。在现代的Haskell中, Monad是由bind( >>=)定义的,从中可以得出 join,但是也可以从 >>=得出 join

IO作为所有值

在这一点上,您可能想知道: IO与到底有什么关系? IO a是否不是具有 a类型的单个值的容器?

并不是的。 IO的一种解释是,它是一个容器,其中包含 a类型的任意值。根据这种解释,它类似于量子力学的多世界解释。 IO aa类型的所有可能值的叠加。

在薛定ding最初的思想实验中,盒子里的猫既活着又死了,直到被观察到。这是两个可能的状态的叠加。如果我们考虑一个名为 catIsAlive的变量,它将等同于 TrueFalse的叠加。因此,您可以将 IO Bool视为一组可能的值 {True, False},这些值只会在观察时折叠为单个值。

同样, IO Word8可以解释为所有可能的 Word8值的集合的叠加,即 {0, 1, 2,.. 255}IO Int表示所有可能的 Int值的叠加, IO String表示所有可能的 String值(即无限集),依此类推。

那么,您如何观察其价值呢?

您不提取它,而是在数据容器中工作。如上所示,您可以在其上输入 fmapjoin。因此,您可以将应用程序编写为纯函数,然后使用 fmap>>=join等与不纯值组成。

关于haskell - 如何从不纯方法中返回纯值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51614573/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com