GHCi :sprint has odd/unhelpful behavior for values defined within the REPL
Wanted to use :sprint to help learners visualise thunk evaluation behavior in their data. Ran into some behaviors that a few people I checked with didn't have a good explanation for. I couldn't find anything in the user guide to explain this. I don't think it technically violates Haskell Report requirements, but it makes :sprint considerably less useful if you're teaching somebody non-strictness.
Examples with code in the REPL:
Prelude> let x = [1, 2, 3 :: Integer]
Prelude> :sprint x
x = [1,2,3]
-- errr, what?
Prelude> let x = Just (1 :: Integer)
Prelude> :sprint x
x = Just 1
Prelude> let just = Just
Prelude> let x = just (1 :: Integer)
Prelude> :sprint x
x = _
Prelude> let x = Just (undefined :: Integer)
Prelude> :sprint x
x = Just _
Prelude> let x = just (undefined :: Integer)
Prelude> :sprint x
x = _
Prelude> let x = [1, 2, 3 :: Integer]
Prelude> let y = x
Prelude> :sprint y
y = [1,2,3]
Prelude> let x = 1 : 2 : (3 :: Integer) : []
Prelude> :sprint x
x = [1,2,3]
Prelude> let x = [1] ++ [2] ++ [(3 :: Integer)]
Prelude> :sprint x
x = _
Prelude> let y = (:)
Prelude> let x = 1 `y` (2 `y` ((3 :: Integer) `y` []))
Prelude> :sprint x
x = _
Prelude> x
[1,2,3]
Prelude> :sprint x
x = [1,2,3]
So the behavior here seems to be:
Constructors used directly in the construction of data and are not passed functions (including polymorphic vals awaiting concrete instances)/bottoms are immediately evaluated
Example, but with loading data from a file:
Contents of the file:
x :: Num a => [a]
x = [1, 2, 3]
GHCi session:
Prelude> :t x
x :: Num a => [a]
Prelude> :sprint x
x = _
Prelude> x
[1,2,3]
Prelude> :sprint x
x = _
-- ^^ this is expected
Then when x is loaded from a file, but has a different type:
Prelude> :t x
x :: [Integer]
Prelude> :sprint x
x = _
Prelude> head x
1
Prelude> :sprint x
x = [1,2,3]
-- ^^ this is not
Now, this is a bit confusing. Earlier I was able to get :sprint to return [1, _, _] when I evaluated head x, but a couple hours later when I went to write this ticket, I couldn't reproduce that behavior.
Is there documentation that explains:
- Why data is shown as having been evaluated at time of declaration (seemingly) by :sprint when it's defined in the GHCi
- Why declaring code in GHCi and loading it from a file behaves differently with :sprint (I considered let expression in the implicit GHCi do-block...couldn't find anything to explain this)
- Why evaluating 'head x' forces the other values as well
Are any of these behaviors a bug? If not, are they documented anywhere? Is the "eager" treatment of constructors in GHCi a performance thing? That seems strange given I didn't have -fobject-code turned on.
:sprint not demonstrating semantics that match what I expect from a non-strict language hinders its utility as a teaching tool and means the only robust option for learners that I can find is testing evaluation with bottom values.
-- So that you know i picked "7.8.4" as the version consciously
[callen@atlantis ~/Work/fpbook]$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.8.4
[callen@atlantis ~/Work/fpbook]$ ghci --version
The Glorious Glasgow Haskell Compilation System, version 7.8.4