haskell - fmap 进入 do 块失败并出现打印错误
问题描述
我试图理解为什么我用 do-block 编写的函数不能重写为 fmap 在列表上的类似 lambda 表达式。
我有以下内容:
-- This works
test1 x = do
let m = T.pack $ show x
T.putStrLn m
test1 1
生产
1
但
-- This fails
fmap (\x -> do
let m = T.pack $ show x
T.putStrLn m
) [1..10]
-- And this also fails
fmap (\x -> do
T.putStrLn $ T.pack $ show x
) [1..10]
有错误:
<interactive>:1:1: error:
• No instance for (Show (IO ())) arising from a use of ‘print’
• In a stmt of an interactive GHCi command: print it
我的 putStrLn 在工作和非工作之间是一致的。进口是一样的。我打印所需的 show-pack-putstrln 舞蹈在工作和非工作之间也是一致的。
打印的使用在工作和非工作之间发生了怎样的变化?
更新 1
-- I was also surprised that this fails
fmap (T.putStrLn $ T.pack $ show) [1..10]
-- it seemed as similar as possible to the test1 function but mapped.
<interactive>:1:7: error:
• Couldn't match expected type ‘Integer -> b’ with actual type ‘IO ()’
• In the first argument of ‘fmap’, namely ‘(T.putStrLn $ pack $ show)’
In the expression: fmap (T.putStrLn $ pack $ show) [1 .. 10]
In an equation for ‘it’: it = fmap (T.putStrLn $ pack $ show) [1 .. 10]
• Relevant bindings include it :: [b] (bound at <interactive>:1:1)
<interactive>:1:29: error:
• Couldn't match type ‘() -> String’ with ‘String’
Expected type: String
Actual type: () -> String
• Probable cause: ‘show’ is applied to too few arguments
In the second argument of ‘($)’, namely ‘show’
In the second argument of ‘($)’, namely ‘pack $ show’
In the first argument of ‘fmap’, namely ‘(T.putStrLn $ pack $ show)’
更新 2
-- This lambda returns x of the same type as \x
-- even while incidentally printing along the way
fmap (\x -> do
let m = T.pack $ show x
T.putStrLn $ m
return x
) [1..10]
但也失败了:
<interactive>:1:1: error:
• No instance for (Show (IO Integer)) arising from a use of ‘print’
• In a stmt of an interactive GHCi command: print it
解决方案
你写了:
但是当我将 lambda 的返回类型更改为与以 \x 形式出现的 x 相同时,就像我在更新 2 中所做的那样......
不,不。你没有。lambda 函数返回其最后一个表达式的值。您的 lambda 函数中只有一个表达式——整个do { ... }
块定义了一个值,即 lambda 函数的返回值。不是x
。return
属于,而do
不是 lambda 表达式。如果我们用显式分隔符来写它更容易看出,因为
fmap (\x -> do {
let m = T.pack $ show x ;
T.putStrLn $ m ;
return x
} ) [1..10]
该do
块作为一个整体具有与其每个行语句相同的一元类型。
其中之一是putStrLn ...
,其类型是IO ()
。所以你的 lambda 函数会返回IO t
一些t
.
并且因为return x
,t
是 的类型x
。我们有return :: Monad m => t -> m t
,所以m ~ IO
也有return :: t -> IO t
。
x
来自参数列表Num t => [t]
,所以总的来说你有
Num t => fmap (fx :: t -> IO t) (xs :: [t]) :: [IO t]
或者
xs :: [t]
fx :: t -> IO t
----------------------------
fmap fx xs :: [IO t]
推荐阅读
- html - 我有多个 HTML 5
- c++ - 我可以创建一个包含两个变量的 for 循环并且仍然具有 O(n) 的时间复杂度吗?
- bash - 从 POSIX 中的函数解析数据
- cookies - 如何使用 apollo 服务器设置带有令牌的 cookie?
- python - 随机森林分类器 ValueError:输入包含 NaN、无穷大或对于 dtype('float32') 来说太大的值
- python - 构造限制矩阵,使得奇数行为零
- gcc - 如何将 SDL .a 文件与 GCC 链接?
- ruby-on-rails - Rails:服务更改直到重新启动才反映
- ruby-on-rails - 如何在 Rails 模型中访问 ActiveStorage 临时文件?
- matlab - MATLAB 中同一图形的 imshow 和 imwrite 不匹配