fmap进入do块失败,并显示打印错误

时间:2019-04-25 16:36:15

标签: haskell monads io-monad do-notation

我试图理解为什么我用do-block编写的函数不能被重写为将类似的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

2 个答案:

答案 0 :(得分:1)

fmap f [1..10]的类型为[T],其中Tf的返回类型。

在您的情况下为T = IO (),因此完整表达式的类型为[IO ()]

无法打印IO操作,因此当您尝试打印该列表时,GHCi会抱怨。您可能想使用sequence_ (fmap f [1..10])之类的方法来运行这些操作而不是打印它们。

或者,考虑放弃fmap,而改用类似的

import Data.Foldable (for_)

main = do
   putStrLn "hello"
   for_ [1..10] $ \i -> do
      putStrLn "in the loop"
      print (i*2)
   putStrLn "out of the loop"

答案 1 :(得分:1)

您写道:

  

但是当我将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 xtx的类型。我们有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]