Ber*_*ian 2 monads haskell functor
我知道它听起来一定是微不足道的,但我想知道如何从仿函数中展开一个值并将其作为纯值返回?
我试过了:
f::IO a->a
f x=(x>>=)
f= >>=
Run Code Online (Sandbox Code Playgroud)
我应该在右侧放置什么?我不能使用,return
因为它会再次包裹它.
Mar*_*ann 10
这是一个经常被问到的问题:如何从我的monad中提取'the'值,不仅在Haskell中,而且在其他语言中.我有一个关于为什么这个问题不断涌现的理论,所以我会试着回答这个问题; 我希望它有所帮助.
您可以将仿函数(也因此也是monad)视为值的容器.这是(冗余)Identity
仿函数最明显的:
Prelude Control.Monad.Identity> Identity 42
Identity 42
Run Code Online (Sandbox Code Playgroud)
在这种情况下,这只是一个值的包装器42
.对于此特定容器,您可以提取值,因为它保证在那里:
Prelude Control.Monad.Identity> runIdentity $ Identity 42
42
Run Code Online (Sandbox Code Playgroud)
虽然Identity
看起来相当无用,但您可以找到其他似乎包含单个值的仿函数.例如,在F#中,您经常会遇到像Async<'a>
or Lazy<'a>
这样的容器,它们用于表示异步或延迟计算(Haskell不需要后者,因为默认情况下它是惰性的).
你会发现很多在Haskell其他单值的容器,如Sum
,Product
,Last
,First
,Max
,Min
,等对所有那些是他们换一个值,这意味着你可以提取价值.
我认为当人们第一次遇到仿函数和monad时,他们倾向于以这种方式思考数据容器的概念:作为单个值的容器.
不幸的是,Haskell中的一些常见的monad似乎支持这个想法.例如,Maybe
也是一个数据容器,但可以包含零个或一个值.不幸的是,如果存在价值,你仍然可以提取价值:
Prelude Data.Maybe> fromJust $ Just 42
42
Run Code Online (Sandbox Code Playgroud)
这个问题fromJust
是不完全的,所以如果用一个Nothing
值调用它会崩溃:
Prelude Data.Maybe> fromJust Nothing
*** Exception: Maybe.fromJust: Nothing
Run Code Online (Sandbox Code Playgroud)
你可以看到同样的问题Either
.虽然我不知道提取Right
值的内置部分函数,但您可以轻松地使用模式匹配编写一个(如果忽略编译器警告):
extractRight :: Either l r -> r
extractRight (Right x) = x
Run Code Online (Sandbox Code Playgroud)
同样,它适用于"快乐路径"场景,但也可以轻松崩溃:
Prelude> extractRight $ Right 42
42
Prelude> extractRight $ Left "foo"
*** Exception: <interactive>:12:1-26: Non-exhaustive patterns in function extractRight
Run Code Online (Sandbox Code Playgroud)
尽管如此,由于fromJust
存在这样的函数,我认为它会让人们对仿函数和monad的概念产生新的想法,将它们视为数据容器,您可以从中提取值.
当你IO Int
第一次遇到这样的事情的时候,我就能明白为什么你会把它想象成一个单一价值的容器.从某种意义上说,它是,但在另一种意义上,它不是.
即使使用列表,您也可以(尝试)从列表中提取"值":
Prelude> head [42..1337]
42
Run Code Online (Sandbox Code Playgroud)
它仍然可能失败:
Prelude> head []
*** Exception: Prelude.head: empty list
Run Code Online (Sandbox Code Playgroud)
然而,在这一点上,应该清楚的是,尝试从任意任意函子中提取"the"值是无稽之谈.列表是一个仿函数,但它包含任意数量的值,包括零和无限多个.
你有什么可以永远做,虽然是写的是采取"包含"值作为输入功能并返回另一个值作为输出.这是这样一个函数的任意例子:
countAndMultiply :: Foldable t => (t a, Int) -> Int
countAndMultiply (xs, factor) = length xs * factor
Run Code Online (Sandbox Code Playgroud)
虽然你不能"提取的值"出来的清单,你可以申请你的函数每个在列表中的值:
Prelude> fmap countAndMultiply [("foo", 2), ("bar", 3), ("corge", 2)]
[6,9,10]
Run Code Online (Sandbox Code Playgroud)
既然IO
是一个仿函数,你也可以用它来做同样的事情:
Prelude> foo = return ("foo", 2) :: IO (String, Int)
Prelude> :t foo
foo :: IO (String, Int)
Prelude> fmap countAndMultiply foo
6
Run Code Online (Sandbox Code Playgroud)
关键是你没有从仿函数中提取值,你进入仿函数.
有时,应用于仿函数的函数会返回已包含在同一数据容器中的值.例如,您可能有一个将字符串拆分为特定字符的函数.为了简单起见,让我们看一下words
将字符串拆分为单词的内置函数:
Prelude> words "foo bar"
["foo","bar"]
Run Code Online (Sandbox Code Playgroud)
如果您有一个字符串列表,并应用于words
每个字符串,您将获得一个嵌套列表:
Prelude> fmap words ["foo bar", "baz qux"]
[["foo","bar"],["baz","qux"]]
Run Code Online (Sandbox Code Playgroud)
结果是一个嵌套的数据容器,在本例中是一个列表列表.您可以使用以下方法展平它join
:
Prelude Control.Monad> join $ fmap words ["foo bar", "baz qux"]
["foo","bar","baz","qux"]
Run Code Online (Sandbox Code Playgroud)
这是monad的原始定义:它是一个可以展平的仿函数.在现代Haskell中,Monad
由bind(>>=
)定义,可以从中派生出来join
,但也可以从中派生>>=
出来join
.
此时,您可能想知道:这与此有什么关系IO
?不是IO a
该类型的单个值的容器a
?
并不是的.一个解释的IO
是,它是保存类型的任意值的容器a
.根据这种解释,它类似于多世界对量子力学的解释.IO a
是所有可能的类型值的叠加a
.
在薛定谔最初的思想实验中,盒子里的猫既活着也死了,直到被观察到.这是叠加的两种可能状态.如果我们考虑一个变量叫catIsAlive
,这将是相当于叠加True
和False
.因此,您可以将其IO Bool
视为一组可能的值{True, False}
,这些值在观察时只会折叠为单个值.
同样,IO Word8
可以解释为所有可能Word8
值的集合的叠加,即{0, 1, 2,.. 255}
,IO Int
作为所有可能Int
值的叠加,IO String
作为所有可能的String
值(即,无限集),等等.
那么你如何观察价值呢?
您不提取它,您在数据容器中工作.你可以,如上图所示,fmap
并join
在其上.所以,你可以写你的应用程序作为纯函数,你再与不纯的值与组合fmap
,>>=
,join
,等等.