9

fix f = let {x = f x} in x

Talking about let, I thought that let P = Q in R would evaluate Q -> Q' then P is replaced by Q' in R, or: R[P -> Q'].

But in fix definition the Q depends on R, how to evaluate then?

I imagine that this is about lazy evaluation. Q' becomes a thunk, but I can't reason this in my head.

As a matter of context, I'm looking at Y combinator, it should find a fixed point of a function so if I have this function, one x = 1, then fix one == 1 must hold, right?

So fix one = let {x = one x} in x, but I can't see how 1 would emerge from that.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
geckos
  • 5,687
  • 1
  • 41
  • 53

1 Answers1

11

Talking about let, I thought that let P = Q in R would evaluate Q -> Q' then P is replaced by Q' in R, or: R[P -> Q'].

Morally, yes, but P is not immediately evaluated, it is evaluated when needed.

But in fix definition the Q depends on R, how to evaluate then?

Q does not depend on R, it depends on P. This makes P depend on itself, recursively. This can lead to several different outcomes. Roughly put:

  • If Q can not return any part of its result before evaluating P, then P represents an infinitely recursing computation, which does not terminate. For example,

    let x = x + 1 in x     -- loops forever with no result
    -- (GHC is able to catch this specific case and raise an exception instead,
    -- but it's an irrelevant detail)
    
  • If Q can instead return a part of its result before needing to evaluate P, it does so.

    let x = 2 : x in x
    -- x = 2 : .... can be generated immediately
    -- This results in the infinite list 2:2:2:2:2:.....
    
    let x = (32, 10 + fst x) in x
    -- x = (32, ...) can be generated immediately
    -- hence x = (32, 10 + fst (32, ...)) = (32, 10+32) = (32, 42)
    

I imagine that this is about lazy evaluation. Q' becomes a thunk, but I can't reason this in my head.

P is associated with a thunk. What matters is whether this thunk calls itself before returning some part of the output or not.

As a matter of context, I'm looking at Y combinator, it should find a fixed point of a function so if I have this function. one x = 1, then fix one == 1 must hold, right?

Yes.

So fix one = let x = one x in x, but I can't see why 1 would emerge from that

We can compute it like this:

fix one
 = {- definition of fix -}
let x = one x in x
 = {- definition of x -}
let x = one x in one x
 = {- definition of one -}
let x = one x in 1
 = {- x is now irrelevant -}
1

Just expand the definitions. Keep recursive definitions around in case you need them again.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
chi
  • 111,837
  • 3
  • 133
  • 218
  • Amazing, thank you so much,. This was equational reasoning? I need to practice that – geckos Dec 03 '20 at 10:47
  • @geckos Yes, that's equational reasoning. Keep in mind that GHC might not follow those steps exactly (its runtime system is rather complex, using a ton of optimizations), but it is guaranteed to lead the the same result. – chi Dec 03 '20 at 10:57
  • 1
    (I was too hasty in posting my answer before seeing the ending of your post... :)) – Will Ness Dec 03 '20 at 17:24
  • _Q does not depend on R, it depends on P._ this is still gray to me, can you explaing more about it? To me P is like a label to Q' in R – geckos Dec 03 '20 at 23:19
  • @geckos Consider `let x = f x in g x`. `f x` depends on `x`, not on `g x`. – chi Dec 04 '20 at 08:50
  • Hmmmmm, I think I get it `let x' = f x in g x''`. Supposing that all x are the same, I just mark it with ' to speak of each one. Now f depends on x, x' depends on f x and x'' depends on x' and g depends on x'' is this right? – geckos Dec 04 '20 at 10:02
  • 1
    @geckos Yes. To be pedantic I would say that `f x` depends on `x`, not that `f` depends on `x`. You say the latter only because you know that `f` is applied to `x`, so you mention `f` but you are thinking of `f x`. – chi Dec 04 '20 at 11:45
  • Oh, sure f x depends on x, my bad. The only thing that doesn't fit in my head is, where the first x (in f x) is coming from. In fix definition for example, I can't see where x is coming from. In another language I would claim that x is undefined – geckos Dec 04 '20 at 22:36
  • 1
    @geckos It is a recursive definition, so you use it before it's fully defined. In imperative procedural languages you can write a function `def x(): if ... then use x() else ...` which calls itself. In Haskell, you can do that even for non-functions (because Haskell is lazy). – chi Dec 05 '20 at 09:24
  • @chi oh, now make sense, thank you chi, is really hard to get this lazy evaluation in head after years using strict languages – geckos Dec 05 '20 at 11:18