Maybe I should have said up front exactly what I’m trying to do.
I’m trying to create a lazy computation data type like this:
struct Deferred{T}
func::Function
item::Base.RefValue{T}
Deferred(f::Function, ::Type{T}=Any) where {T<:Any} = new{T}(f, Base.RefValue{T}())
end
function (pc::Deferred{T})()::T where {T<:Any}
if !isassigned(pc.item)
pc.item[] = pc.func()
end
return pc.item[]
end
which can be used like this:
julia> df = Deferred(String) do
sleep(1.0) # placeholder for some expensive computation
"Fish or fowl"
end
julia> df()
Except I thought it would be nice to not have to specify the type in the constructor but rather have it determined by the function’s (or closure’s) return type.
This data type would be useful for me because it would allow me to defer calculation of an expensive member of a computation result until (if ever) it is required. (Deferred{T}
is intended to be used as a field within an encompassing struct
.)
It sounds like there is a non-public API to access the return type either
Base.return_types(fx)[1]
or
Core.Compiler.return_type(fx)
but using these hidden methods is discouraged.
A more complete example of how it could be used:
struct Demo{T, U}
item1::Deferred{T}
item2::Deferred{U}
function Demo(d1::Deferred{V}, d2::Deferred{W}) where { V <: Any, W <: Any }
new{V,W}(d1,d2)
end
end
fn(n) = n ∈ (0, 1) ? 1 : fn(n-2) + fn(n-1) # slow!
n, str = 42, "It's a bird!"
dd = Demo(Deferred(String) do
Base.sleep(2)
str
end,
Deferred(Int) do
fn(n)
end
)
@test !isassigned(dd.item1)
@test dd.item1() == "It's a bird!"
@test isassigned(dd.item1)
@test !isassigned(dd.item2)
@test dd.item2() == 433494437
@test isassigned(dd.item2)
I’d like to eliminate the String
and Int
in the Deferred
constructor since they are redundant.