#imports
let sine = proc(v: float): float {.closure.} =
sin(v)
let time = proc(v: float): float {.closure.} =
epochTime()
proc compose[T](fns: varargs[proc(v: T): T]): proc(v: T): T =
foldr(@fns, proc(arg: T): T = a(b(arg)))
let sineWave = compose[float](sine, time)
echo sineWave(0) #gives sin(t)
However, this results in a stack overflow:
#imports
proc gain(s: float): proc(v: float): float =
return proc(v: float): float =
v * s
let sine = proc(v: float): float {.closure.} =
sin(v)
let time = proc(v: float): float {.closure.} =
epochTime()
proc compose[T](fns: varargs[proc(v: T): T]): proc(v: T): T =
foldr(@fns, proc(arg: T): T = a(b(arg)))
let g = gain(0.5)
let dampedSineWave = compose[float](g, sine, time)
echo dampedSineWave(0)
How come?
Not sure about the stack overflow but allocating closures at runtime is inefficent.
I would rewrite your compose with macro:
import macros, math
macro compose(procName: untyped, fns: varargs[untyped]): untyped =
# echo fns.treerepr
let input = genSym(nskParam, "input") # input of the new proc
var procBody = newStmtList().add input
var i = fns.len
while i > 0:
dec i
procBody = newCall(fns[i], procBody)
# echo procBody.treerepr
result = quote do:
proc `procName`[T](`input`: T): T =
`procBody`
compose(sincos, sin, cos)
echo sincos(Pi) # -0.8414709848078965
This create a sincos function which does sin(cos()) without having to pay for closures.