Search code examples
functionhaskellvisual-studio-codefirst-class-functions

Type mismatch on basic "apply twice" hello world in Haskell


The minimal code:

twice :: (a -> a) -> a -> a
twice f = f . f

main = do
   return twice ++ "H"

The errors generated:

stack runhaskell "c:\Users\FruitfulApproach\Desktop\Haskell\test.hs"

C:\Users\FruitfulApproach\Desktop\Haskell\test.hs:5:1: error:
    * Couldn't match expected type `IO t0'
                  with actual type `[(a0 -> a0) -> a0 -> a0]'
    * In the expression: main
      When checking the type of the IO action `main'
  |
5 | main = do
  | ^

C:\Users\FruitfulApproach\Desktop\Haskell\test.hs:6:20: error:
    * Couldn't match type `Char' with `(a -> a) -> a -> a'
      Expected type: [(a -> a) -> a -> a]
        Actual type: [Char]
    * In the second argument of `(++)', namely `"H"'
      In a stmt of a 'do' block: return twice ++ "H"
      In the expression: do return twice ++ "H"
    * Relevant bindings include
        main :: [(a -> a) -> a -> a]
          (bound at C:\Users\FruitfulApproach\Desktop\Haskell\test.hs:5:1)
  |
6 |    return twice ++ "H"
  |                    ^^^

How would I logically fix this issue myself? Clearly it's something I'm doing wrong. Am I missing a preamble that every example should have?


Solution

  • As RobinZigmond mentions in the comments, you can’t write twice ++ "H". This means, ‘take the function twice, and append the string "H" to it’. This is clearly impossible, since ++ can only append strings and lists together. I suspect that what you meant was twice (++ "H"). This takes the function (++ "H"), which appends "H" to the end of its argument, and runs it twice.

    But even if you do this, there is still a problem. Take a look at the program which is created if you do this:

    twice :: (a -> a) -> a -> a
    twice f = f . f
    
    main = do
       return (twice (++ "H"))
    

    Even though this program compiles, it doesn’t do anything! You have set twice (++ "H")) as the return value of main, but the return value of main is always ignored. In order to produce output, you need to use putStrLn instead of return:

    twice :: (a -> a) -> a -> a
    twice f = f . f
    
    main = do
       putStrLn (twice (++ "H"))
    

    But this program doesn’t work either! twice (++ "H") is a function, which cannot be printed. This function must be applied to a value in order to produce a result:

    twice :: (a -> a) -> a -> a
    twice f = f . f
    
    main = do
       putStrLn (twice (++ "H") "Starting value")
    

    This program should finally work, giving an output of Starting valueHH when it is run.