Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lazy evaluations of data structures

I'm reading about lazy evaluations in haskell and have a question. For example we have following computations:

Prelude> let x = 1 + 1 :: Int
Prelude> let y = (x,x)

And after getting value of x:

Prelude> :sprint x
x = _

It's unevaluated. Ok, now let's get value of y:

Prelude> :sprint y
y = (_,_)

It is unevaluated too, because y depends on x and it's unevaluated. Now let's try the same example but without ::Int:

Prelude> let x = 1 + 1
Prelude> let y = (x, x)
Prelude> :sprint y
y = _

Why y value is _ instead (_, _) when we're trying without ::Int?

I see that they have different types:

Prelude> let x = 1 + 1
Prelude> :t x
x :: Num a => a
Prelude> let x = 1 + 1 :: Int
Prelude> :t x
x :: Int

But why values of y depends on it?

Thank you.

like image 587
0xAX Avatar asked Jul 25 '14 18:07

0xAX


People also ask

What is lazy evaluation example?

Lazy evaluation is an evaluation strategy which holds the evaluation of an expression until its value is needed. It avoids repeated evaluation. Haskell is a good example of such a functional programming language whose fundamentals are based on Lazy Evaluation.

Why spark is called lazy evaluation?

As the name itself indicates its definition, lazy evaluation in Spark means that the execution will not start until an action is triggered. In Spark, the picture of lazy evaluation comes when Spark transformations occur.

How is lazy evaluation implemented?

To implement lazy evaluation in our interpreter we need to modify the applica- tion expression evaluation rule to delay evaluating the operand expressions until they are needed. To do this, we introduce a new datatype known as a thunk. We define a Python class, Thunk for representing thunks.

Why is lazy evaluation a powerful advantage of functional programs?

Lazy Evaluation One of the most significant advantages of functional programming is that it only stores values when needed. There is a thorough evaluation that goes into the storage of the value. Doing so avoids repeated evaluation of the functional program's inputs.


1 Answers

What is happening is that when you've specified x to have the type Num a => a, the compiler can't possibly know which instance of Num to use when performing 1 + 1. What it does instead is use defaulting. GHC defines default types for certain typeclasses so that when there's no possible way to determine what concrete type to use it can still give meaningful results without raising errors. So when you see

> let x :: Num a => a
|     x = 1 + 1
> x
2
> :sprint x
x = _

This is because GHCi chooses Integer as its default type for Num, but when it performs this operation it doesn't store the result in x's memory location, since there isn't a way to know if that is even the correct answer. This is why you see x = _ from :sprint, it hasn't actually evaluated x :: Num a => a, it's evaluated x :: Integer. You can even mess with this default yourself:

> newtype MyInt = MyInt Int deriving (Eq)
>
> instance Show MyInt where
|     show (MyInt i) = show i
> instance Num MyInt where
|     (MyInt x) + (MyInt y) = MyInt (x - y)
|     fromInteger = MyInt . fromInteger
>
> default (MyInt)
> x
0

So now we've said that 1 + 1 = 0! Keep in mind that you will probably never have a use for this functionality of GHC, but it's good to know about.

like image 97
bheklilr Avatar answered Sep 23 '22 06:09

bheklilr