fmap into a do block fails with a print error

大城市里の小女人 提交于 2019-12-24 07:19:24

问题


I'm trying to understand why a function I have written with a do-block can't be rewritten to fmap a similar lambda expression over a list.

I have the following:

-- This works
test1 x = do 
        let m = T.pack $ show x
        T.putStrLn m

test1 1

Produces

1

But

-- 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]

With error:

<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

My putStrLn is consistent between the working and the non-working. The imports are the same. My show-pack-putstrln dance required to print is also consistent between the working and the non-working.

What is happening that the use of print is changing between the working and non-working?

Update 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)’

Update 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]

But also fails with:

<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

回答1:


The type of fmap f [1..10] is [T] where T is the return type of f.

In your case, T = IO (), so the type of the full expression is [IO ()].

IO actions can not be printed, so GHCi complains when you try to print that list. You might want to run those actions instead of printing them, using something like sequence_ (fmap f [1..10]).

Alternatively, consider ditching fmap and instead using something like

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"



回答2:


You wrote:

but when I change the return type of the lambda to be the same as the x that comes in as \x as I do in the update 2 ...

No, no. You don't. A lambda function returns the value of its last expression. Your lambda function has just one expression in it -- the entire do { ... } block defines a value, which is that lambda function's return value. Not x. The return belongs to the do, not the lambda expression. It is easier to see if we write it with the explicit separators, as

fmap (\x -> do {
              let m = T.pack $ show x ;
              T.putStrLn $ m ;
              return x
              } ) [1..10]

The do block as a whole has the same monadic type as each of its line statements.

One of those is putStrLn ..., whose type is IO (). So your lambda function returns IO t for some t.

And because of return x, t is the type of x. We have return :: Monad m => t -> m t, so with m ~ IO it is return :: t -> IO t.

x comes from the argument list Num t => [t], so overall you have

Num t => fmap (fx :: t -> IO t) (xs :: [t]) :: [IO t]

or

           xs :: [t]
        fx    ::  t  ->  IO t
   ----------------------------
   fmap fx xs ::        [IO t]


来源:https://stackoverflow.com/questions/55853955/fmap-into-a-do-block-fails-with-a-print-error

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!