Skip to content

tbenst/Thunks.jl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

68 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Thunks.jl

Documentation Build Status Coverage

Thunks.jl provides a simple implementation of a Thunk for lazy computation, and a sophisticated macro @lazy that rewrites arbitrary Julia expressions for a lazy evaluation strategy.

A thunk represents a computation that is not run until we reify it, meaning make "real". Once reified, the thunk caches the value of the computation. The core implementation is only 30 LOC, so consider taking a peak. Most of the complexity lies in the @lazy macro, which supports lazy evaluation of nearly any Julia expression, including dot broadcasting, indexing, keyword arguments, if blocks, comprehensions, and more. Thunks can be composed, meaning that you can transform existing julia code for lazy evaluation just by prepending @lazy.

The implementation approximates laziness in pure functional languages like Haskell: a memoizing call-by-need. This means that a computation captured in a thunk is run 0 or 1 times, with subsequent calls re-using ("sharing") the result of the previous evaluation.

Installation

julia> ] add Thunks

Usage

Note that the below example will execute nearly instantly due to laziness, whereas the eager equivalent would take a minute.

w = thunk(sleep)(60)
x = thunk(identity)(2)
# equivalent to line above
y = @lazy identity(2)
# also equivalent
@lazy yy = identity(2)
z = thunk(+)(x, y)
@assert z.evaluated == false
@assert reify(z) == 4
@assert z.evaluated == true
@assert w.evaluated == false

The @lazy macro also supports code blocks:

@lazy begin
    w = sleep(60)
    a = 2
    b = 3
    c = 1
    abc = sum([a,b,c])
end
@assert typeof(w) == Thunk
@assert typeof(a) == Int
@assert typeof(abc) == Thunk
@assert reify(abc) == 6

@lazy aims to support arbitrary Julia expressions:

test() = (1,2,3)
@lazy begin
    a = true ? (()-> ones(5))() : zeros(5)
    b = a .+ a
    c = collect(1:b[3]*5)[7:end]
    d = identity(6)
    e = [x[1:2] for x in repeat([repeat([d], 3)],3)]
end
@assert reify(c) == [7,8,9,10]
@assert e.evaluated == false
@assert all(sum(reify(e)) .== [18, 18])
@assert e.evaluated == true

More usage examples can be seen in the tests.

Limitations

Currently, using @lazy on nested blocks is not supported.

Acknowledgements

Thunks.jl is inspired by the Thunk implementation of the fantastic Dagger.jl and is intended as a lightweight, more performant alternative without the scheduling capabilities.