Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding how :sprint and list evaluation works in haskell

I'm working through haskell book and I understood that :sprint x is used to print the elements of x has been evaluated and the elements has been not(the ones which aren't are expressed by an '_').

One of the examples provided in the book,

Prelude> let blah = enumFromTo 'a' 'z' 
Prelude> :sprint blah 
blah = _

Prelude> take 1 blah 
"a" 
Prelude> :sprint blah 
blah = 'a' : _

To test out with different input i did this in GHCi:-

prelude> let b = [1,2,3,4,5]
prelude> :sprint b
b = _
prelude> take 1 b
[1]
prelude> :sprint b
b = _

Shouldn't the output of :sprint b in the last command be b = 1 : _ since we are only evaluating a single list item and a cons operator when using the command take 1 b ?? But it's showing the output above. How and why is this happening ? Shouldn't the output be similar to the output of String type?

Edit: I've been experimenting more and got this result :-

prelude> let b = [1..10] :: [Int]
prelude> :sprint b
b = _
prelude> take 3 b
[1,2,3]
prelude> :sprint b
b = 1 : 2 : 3 : _

Okay, my initial guess is, it's because of the way I'm constructing the two lists ? One is using ranges and the other by explicitly stating its elements (which in turns creates the list by using the cons ':' constructor recursively on its elements)

like image 478
Chakravarthy Raghunandan Avatar asked Mar 12 '23 09:03

Chakravarthy Raghunandan


1 Answers

The behavior of :sprint can be a little tricky. In this case, look at the type of x:

> :t x
x :: Num t => [t]

Because it's polymorphic, the actual values that get produced depend on the specific instance of Num that you require. Thus, x behaves more like a function that will produce the list [1,2,3,4,5] when it can figure out just what type you want the elements to have.

Now you might think, "ok, I'll make a list that isn't polymorphic", so you try this:

> let x = [1,2,3,4,5 :: Int]
> :t x
x :: [Int]
> :sprint x
x = [1,2,3,4,5]

What the heck? If you think about it, this makes sense. We've told ghci explicitly what the list is. It's not like it can unevaluate it and then reevaluate it later. (This would be wasteful anyway.) But let's see what happens when we try to map a function over x:

> let y = map (+1) x
> :sprint y
y = _
> take 1 y
[2]
> :sprint y
y = 2 : _

Just as expected!

Hope that helps. Lazy evaluation is one of the trickier things about Haskell (when it becomes important).

like image 170
user2297560 Avatar answered Mar 24 '23 05:03

user2297560