Tiny Ollibot Example Server - Call-by-need functions

Call-by-need functions

Robert J. Simmons, Frank Pfenning

This example is derived from Figure 9 in Substructural Operational Semantics as Ordered Logic Programming.

This specification of call-by-need functions is a modification of call-by-name with destinations for binding that enforces that a function argument will be evaluated at most once, and only when it is needed. The rules for evaluating lambdas and applications are the same as in call-by-name with binding, with the exception that the rule eapp2 that returns a function to the application associates the function's argument not with the persistent atomic propostion !bind D2 E2 but with the linear atomic proposition ¡susp D2 E2.

elam : eval(lam λx. E x) ->> return(lam λx. E x).
eapp1 : eval(app E1 E2) ->> comp(app1 E2) • eval(E1).
eapp2 : comp(app1 E2) • return(lam λx. E1' x)
   ->> ∃D2. eval(E1' D2) • ¡susp D2 E2.

The real interesting action happens with binding.

evar1 : eval(D) • ¡susp D E ->> comp(bind1 D) • eval(E).
evar2 : comp(bind1 D) • return(V) ->> return(V) • !bind D V.
evar3 : eval(D) • !bind D V ->> return(V).

Evaluation examples

We first show the evaluation of the simple term (λx.x)((λy.y)(λz.z)), which evaluates to a value in 11 steps.

%trace * eval(app (lam λx.x) (app (lam λy.y) (lam λz.c))).
eval(app (lam(λx. x)) (app (lam(λy. y)) (lam(λz. c))))
comp(app1(app (lam(λy. y)) (lam(λz. c))))eval(lam(λx. x))
comp(app1(app (lam(λy. y)) (lam(λz. c))))return(lam(λx. x))
¡susp d6 (app (lam(λy. y)) (lam(λz. c)))eval(d6)
comp(bind1(d6))eval(app (lam(λy. y)) (lam(λz. c)))
comp(bind1(d6))comp(app1(lam(λz. c)))eval(lam(λy. y))
comp(bind1(d6))comp(app1(lam(λz. c)))return(lam(λx. x))
¡susp d7 (lam(λz. c))comp(bind1(d6))eval(d7)
comp(bind1(d6))comp(bind1(d7))eval(lam(λz. c))
comp(bind1(d6))comp(bind1(d7))return(lam(λx. c))
!bind d7 (lam(λx. c))comp(bind1(d6))return(lam(λx. c))
!bind d6 (lam(λx. c))!bind d7 (lam(λx. c))return(lam(λx. c))

Call-by-need has a particular interesting behavior: some non-termination that happens due to recursion can be detected at runtime! The particular type of non-termination

%trace * eval (app (lam λx. app x x) (app (lam λy.y) (lam λz.z))).
eval(app (lam(λx. app x x)) (app (lam(λy. y)) (lam(λz. z))))
comp(app1(app (lam(λy. y)) (lam(λz. z))))eval(lam(λx. app x x))
comp(app1(app (lam(λy. y)) (lam(λz. z))))return(lam(λx. app x x))
¡susp d8 (app (lam(λy. y)) (lam(λz. z)))eval(app d8 d8)
¡susp d8 (app (lam(λy. y)) (lam(λz. z)))comp(app1(d8))eval(d8)
comp(app1(d8))comp(bind1(d8))eval(app (lam(λy. y)) (lam(λz. z)))
comp(app1(d8))comp(bind1(d8))comp(app1(lam(λz. z)))eval(lam(λy. y))
comp(app1(d8))comp(bind1(d8))comp(app1(lam(λz. z)))return(lam(λx. x))
¡susp d9 (lam(λz. z))comp(app1(d8))comp(bind1(d8))eval(d9)
comp(app1(d8))comp(bind1(d8))comp(bind1(d9))eval(lam(λz. z))
comp(app1(d8))comp(bind1(d8))comp(bind1(d9))return(lam(λx. x))
!bind d9 (lam(λx. x))comp(app1(d8))comp(bind1(d8))return(lam(λx. x))
!bind d8 (lam(λx. x))!bind d9 (lam(λx. x))comp(app1(d8))return(lam(λx. x))
!bind d8 (lam(λx. x))!bind d9 (lam(λx. x))¡susp d10 d8eval(d10)
!bind d8 (lam(λx. x))!bind d9 (lam(λx. x))comp(bind1(d10))eval(d8)
!bind d8 (lam(λx. x))!bind d9 (lam(λx. x))comp(bind1(d10))return(lam(λx. x))
!bind d10 (lam(λx. x))!bind d8 (lam(λx. x))!bind d9 (lam(λx. x))return(lam(λx. x))
%trace 10 eval(
  app (app 
        (lam λf . app (lam λx . app f (app x x)) (lam λx. app f (app x x)))
        (lam λx. x))
eval(app (app (lam(λf. app (lam(λx. app f (app x x))) (lam(λx. app f (app x x))))) (lam(λx. x))) unknown)
comp(app1(unknown))eval(app (lam(λf. app (lam(λx. app f (app x x))) (lam(λx. app f (app x x))))) (lam(λx. x)))
comp(app1(unknown))comp(app1(lam(λx. x)))eval(lam(λf. app (lam(λx. app f (app x x))) (lam(λx. app f (app x x)))))
comp(app1(unknown))comp(app1(lam(λx. x)))return(lam(λx. app (lam(λx1. app x (app x1 x1))) (lam(λx1. app x (app x1 x1)))))
¡susp d11 (lam(λx. x))comp(app1(unknown))eval(app (lam(λx. app d11 (app x x))) (lam(λx. app d11 (app x x))))
¡susp d11 (lam(λx. x))comp(app1(unknown))comp(app1(lam(λx. app d11 (app x x))))eval(lam(λx. app d11 (app x x)))
¡susp d11 (lam(λx. x))comp(app1(unknown))comp(app1(lam(λx. app d11 (app x x))))return(lam(λx. app d11 (app x x)))
¡susp d12 (lam(λx. app d11 (app x x)))¡susp d11 (lam(λx. x))comp(app1(unknown))eval(app d11 (app d12 d12))
¡susp d12 (lam(λx. app d11 (app x x)))¡susp d11 (lam(λx. x))comp(app1(unknown))comp(app1(app d12 d12))eval(d11)
¡susp d12 (lam(λx. app d11 (app x x)))comp(app1(unknown))comp(app1(app d12 d12))comp(bind1(d11))eval(lam(λx. x))
¡susp d12 (lam(λx. app d11 (app x x)))comp(app1(unknown))comp(app1(app d12 d12))comp(bind1(d11))return(lam(λx. x))
%trace 13 eval(app (lam λx. app x x) (lam λy. app y y)).
eval(app (lam(λx. app x x)) (lam(λy. app y y)))
comp(app1(lam(λy. app y y)))eval(lam(λx. app x x))
comp(app1(lam(λy. app y y)))return(lam(λx. app x x))
¡susp d13 (lam(λy. app y y))eval(app d13 d13)
¡susp d13 (lam(λy. app y y))comp(app1(d13))eval(d13)
comp(app1(d13))comp(bind1(d13))eval(lam(λy. app y y))
comp(app1(d13))comp(bind1(d13))return(lam(λx. app x x))
!bind d13 (lam(λx. app x x))comp(app1(d13))return(lam(λx. app x x))
!bind d13 (lam(λx. app x x))¡susp d14 d13eval(app d14 d14)
!bind d13 (lam(λx. app x x))¡susp d14 d13comp(app1(d14))eval(d14)
!bind d13 (lam(λx. app x x))comp(app1(d14))comp(bind1(d14))eval(d13)
!bind d13 (lam(λx. app x x))comp(app1(d14))comp(bind1(d14))return(lam(λx. app x x))
!bind d14 (lam(λx. app x x))!bind d13 (lam(λx. app x x))comp(app1(d14))return(lam(λx. app x x))
!bind d14 (lam(λx. app x x))!bind d13 (lam(λx. app x x))¡susp d15 d14eval(app d15 d15)