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.
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With