3

I am reading the excellent article Understanding map and apply by Scott Wlaschin and running some Haskell code to understand the concepts (Functor, Applicative, ...). I stumbled upon a behaviour I do not understand.

Why evaluating pure add1 prints nothing ? What is the value of the evaluated expression ? Why pure add1 "abc" gives me back the function add1 ?

I understand that pure lifts a value into the elevated world (so called in the article). Since I do not provide a concrete lifted value somewhere or enough type information, the type constraint is general and stays Applicative f. Thus I understand the type of pure add1. But the rest of what's happening here eludes me.

$ stack ghci
GHCi, version 8.8.2
λ: add1 :: Int -> Int ; add1 x = x + 1
λ: :t add1
add1 :: Int -> Int
λ: add1 100
101
λ: :t pure
pure :: Applicative f => a -> f a
λ: pure add1
λ: :t pure add1
pure add1 :: Applicative f => f (Int -> Int)
λ: pure add1 "abc"

<interactive>:8:1: error:
    • No instance for (Show (Int -> Int)) arising from a use of ‘print’
        (maybe you haven't applied a function to enough arguments?)
    • In a stmt of an interactive GHCi command: print it
λ: :t pure add1 "abc"
pure add1 "abc" :: Int -> Int
λ: pure add1 "abc" 100
101

EDIT I think the two comments by @chi and the answer by @sarah answers the question because it shows the applicative chosen by GHCi to evaluate the expression and that explains the observed behaviour.

Ludovic Kuty
  • 4,868
  • 3
  • 28
  • 42
  • Looks like the same question as [ghci special case for Applicative?](https://stackoverflow.com/q/7949464/452614) but it does not allow me to understand `pure add1 "abc"`. – Ludovic Kuty Mar 08 '20 at 07:52
  • 1
    No, I think this is a different question: if it were the same, you would have `pure add1 :: IO (Int -> Int)`, which doesn’t let you do `pure add1 "abc" 100`. But I must say, this behaviour looks incredibly weird — I certainly wouldn’t have a clue how to explain it. – bradrn Mar 08 '20 at 08:03
  • 1
    IO receives special handling in GHCi. Your `pure add1` is considered as a value of type `IO (Int -> Int)`, and as such it is run (no effect). Its final result (`add1`) is then not printed because it is not `Show`able. By contrast, try `pure "hello"` which being a `IO String`, gets its value printed at the end. – chi Mar 08 '20 at 08:47
  • @chi if that was true, `pure add1 "abc"` would not type check. No code in the question involves IO, other than the implicit `print it`. See my answer – sara Mar 08 '20 at 10:38
  • 3
    @sara When you enter `pure add1` in GHCi it is interpreted inside IO. When you enter `pure add1 something`, this is interpreted in the `(->) a` applicative. GHCi's magic tries to use IO at the top level when possible: in the former case it is, in the latter it is not. – chi Mar 08 '20 at 12:06
  • immediately after `λ: pure add1`, type `λ: it 5`. It returns `6`. this means `add1` was returned alright (from `IO`), it just wasn't printed because functions have no printed representation. – Will Ness Mar 08 '20 at 20:38
  • also type `λ: :set +t` to set the option to always see the types of expressions. then `λ: pure add1` prints `it :: Integer -> Integer` which gives us a clue that it returned *something* because `it` refers to the previous result in GHCi. but `λ: x = pure add1` prints something completely different - a polymorphic type. (unless you have `:set -XMonomorphismRestriction`) – Will Ness Mar 08 '20 at 20:46

1 Answers1

5

Since you are applying the expression pure add1 to the value "abc", the Applicative instance gets picked to be the one for (->) String. In that instance, pure = const, so your final expression is const add1 "abc" which is add1, which has no Show instance!

sara
  • 3,521
  • 14
  • 34
  • OK, I will try to process that :-) Note that the show error is not a problem since it is a function which has no instance for show by default. Maybe I shouldn't have left it. My problem was that `const add1 "abc"` gave `add1`, which you have answered. – Ludovic Kuty Mar 08 '20 at 08:24
  • I think I have to better understand type constructors to fully grasp it. However your answer, the comments and the other answer linked in this post provided enough information to start to understand what's going on. I will edit my post when I'm done figuring it out. – Ludovic Kuty Mar 08 '20 at 09:40
  • 1
    I'm currently in bed with a fever, so I wrote the answer on my phone. I might try to elaborate a bit later when I feel better! – sara Mar 08 '20 at 09:46