In implementations where you want named data, I've noticed that the
algorithm gets obfuscated by lots of variable names with dots after them.
For example, here is a basic analog model of a state variable filter used
as a sine wave generator:
immutable SvfSinOscCoef
g0::Float64
g1::Float64
end
immutable SvfSinOsc
ic1eq::Float64
ic2eq::Float64
end
function SvfSinOscCoef_Init (;freq=1.0, sr=44100.0)
local g::Float64 = tan (2pi*freq/sr)
local g0 = 1.0/(1.0+g^2)
SvfSinOscCoef (g0,g*g0)
end
function SvfSinOsc_Init (startphase::Float64)
SvfSinOsc (cos(startphase), sin(startphase))
end
But the tick function looks a bit messy:
function tick (state::SvfSinOsc, coef::SvfSinOscCoef)
local v1::Float64 = coef.g0*state.ic1eq - coef.g1*state.ic2eq
local v2::Float64 = coef.g1*state.ic1eq + coef.g0*state.ic2eq
SvfSinOsc (2*v1 - state.ic1eq, 2*v2 - state.ic2eq)
end
It would be really cool if there was a way to shorthand the syntax of this
to something like the following, which is a lot more readable:
function tick (state::SvfSinOsc, coef::SvfSinOscCoef)
using s, c
local v1::Float64 = g0*ic1eq - g1*ic2eq
local v2::Float64 = g1*ic1eq + g0*ic2eq
SvfSinOsc (2*v1 - ic1eq, 2*v2 - ic2eq)
end
Lots of algorithms have arguments with the same type, but even then you
could still specify using just the most used argument, but if it doesn't
help make things more clear or isn't useful then people don't have to use
it at all.
Another pattern that would be nice to handle cleanly is: fetch state to
local, compute on local, store local to state. I have written code that
generates code to handle this since it is such a pain to keep everything in
sync, but if there was some way to automate this at the language level then
it would really rock, so here is an example of the longhand way, which
isn't too bad for this example, but just imagine if there are 20 or so
variables, and you are writing multiple tick functions:
type SvfSinOsc
ic1eq::Float64
ic2eq::Float64
end
function tick (state::SvfSinOsc, coef::SvfSinOscCoef)
local ic1eq::Float64 = state.ic1eq
local ic2eq::Float64 = state.ic2eq
for i = 1:100
# compute algorithm using local copies of state.ic1eq and
state.ic2eq
end
state.ic1eq = ic1eq
state.ic2eq = ic2eq
return state
end
I have a feeling that macros may be able to help out here to result in
something like:
function tick (state::SvfSinOsc, coef::SvfSinOscCoef)
@fetch state
for i = 1:100
# compute iterative algorithm using local copies of state.ic1eq and
state.ic2eq
end
@store state
return state
end
But I'm not sure how to code such a beast, I tried something like:
macro fetch(obj::SvfSinOsc)
return quote
local ic1eq = obj.ic1eq
local ic2eq = obj.ic2eq
end
end
macro store(obj::SvfSinOsc)
return quote
obj.ic1eq = ic1eq
obj.ic2eq = ic2eq
end
end
dump(osc)
macroexpand (:(@fetch osc))
macroexpand (:(@store osc))
SvfSinOsc
ic1eq: Float64 1.0
ic2eq: Float64 0.0
Out[28]: :($(Expr(:error,
TypeError(:anonymous,"typeassert",SvfSinOsc,:osc))))