Mittenchops
Mittenchops

Reputation: 19664

Failing to putStrLn something just assigned

I have three examples below. I am trying to understand why in the first case, I can assign the variable, but not print it. In the second, if I assign the whole thing as a string, I can print it later just fine. But in the third, if I do the compute and then try to print, this fails:

{-# LANGUAGE DeriveGeneric,  OverloadedStrings, RankNTypes, KindSignatures, FlexibleContexts, AllowAmbiguousTypes #-}
:ext OverloadedStrings 

import Data.Text as T
import Data.Text.IO as T

-- This works
mm1 x = do 
        T.putStrLn "Testing m1"
        let m = T.pack $ show x
            y = x * 2
        T.putStrLn "Here is m"
        T.putStrLn m
        T.putStrLn "y has been assigned successfully, but not printed."

mm1 10

-- This also works
mm2 x = do 
        T.putStrLn "Testing m2"
        let m = T.pack $ show x
            y = T.pack $ show $ x * 2
        T.putStrLn "Here is m"
        T.putStrLn m
        T.putStrLn "Here is y"
        T.putStrLn y  -- this prints fine

mm2 10


-- But This does not work
mm3 x = do 
        T.putStrLn "Testing m3"
        let m = T.pack $ show x
            y = x * 2
        T.putStrLn "Here is m"
        T.putStrLn m
        T.putStrLn "y has been assigned cannot be printed:"
        T.putStrLn T.pack $ show y

mm3 10

My error is:

<interactive>:8:9: error:
    • Couldn't match expected type ‘String -> IO b’ with actual type ‘IO ()’
    • The first argument of ($) takes one argument,
      but its type ‘IO ()’ has none
      In a stmt of a 'do' block: T.putStrLn pack $ show y
      In the expression:
        do T.putStrLn "Testing m3"
           let m = pack $ show x
               y = x * 2
           T.putStrLn "Here is m"
           T.putStrLn m
           ....
    • Relevant bindings include mm3 :: a -> IO b (bound at <interactive>:1:1)
<interactive>:8:20: error:
    • Couldn't match expected type ‘Text’ with actual type ‘String -> Text’
    • Probable cause: ‘pack’ is applied to too few arguments
      In the first argument of ‘T.putStrLn’, namely ‘pack’
      In the expression: T.putStrLn pack
      In a stmt of a 'do' block: T.putStrLn pack $ show y

I don't understand what this error is telling me.

Specifically, if

    let
        y = T.pack $ show $ x * 2
    T.putStrLn y  -- this prints fine

works. Why does:

    let
        y = x * 2
    T.putStrLn T.pack $ show y

fail? These should be exactly identical, right?

Upvotes: 0

Views: 49

Answers (1)

Thomas M. DuBuisson
Thomas M. DuBuisson

Reputation: 64740

No they are not identical.

The first one, inlined, parses as:

T.putStrLn (T.pack (show (x * 2)))

The second one parses as:

(T.putStrLn T.pack) (show (x * 2))

You can't putStrLn of the pack function and then apply that result, as a function, to the input show y. You probably wanted:

T.putStrLn $ T.pack $ show (x * 2)

or

T.putStrLn (T.pack (show (x * 2)))

EDIT: Reading the error message, this is what it's trying to help you understand:

T.putStrLn T.pack $ show y
            ^
             |
     Couldn't match expected type ‘Text’ with actual type ‘String -> Text’

The value T.pack is of type String -> Text but the type checker knows, based on T.putStrLn, this value should be type Text if things are to work.

    T.putStrLn T.pack $ show y
        |
     Couldn't match expected type ‘String -> IO b’ with actual type ‘IO ()’
      • The first argument of ($) takes one argument,
        but its type ‘IO ()’ has none

If the first argument to $, T.putStrLn T.pack, were to type check then the result is dictated by the result of T.putStrLn which is IO (). However, the first argument of $ must "take one argument" (be type a -> b) and IO () clearly does not.

Upvotes: 7

Related Questions