首页 > 解决方案 > 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

标签: haskellmonadsio-monaddo-notation

解决方案


你写了:

但是当我将 lambda 的返回类型更改为与以 \x 形式出现的 x 相同时,就像我在更新 2 中所做的那样......

不,不。你没有。lambda 函数返回其最后一个表达式的值。您的 lambda 函数中只有一个表达式——整个do { ... }定义了一个值,即 lambda 函数的返回值。不是xreturn属于,而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]

推荐阅读