Good pickup Andrew.
Seems a bit redundant to use comprehension here. More concise might be:
macro CallDefaultConstructor(T)
Expr(:call, T, names(eval(T))...)
end
I just copied from part of a larger set of utilities that I use for
managing composite types, where I tend to keep the same design pattern.
function CompositeCopyConstructor(T::Symbol)
expressions = [ :(copy(x.$field)) for field in names(eval(T)) ]
body = Expr(:call, T, expressions...)
quote
function $T(x::$T)
return $body
end
end
end
function CompositeBinaryOp(T::Symbol, op::Symbol)
expressions = [ :($op(x1.$field, x2.$field)) for field in names(eval(T))
]
body = Expr(:call, T, expressions...)
quote
function $op(x1::$T, x2::$T)
return $body
end
end
end
function CompositeInPlaceUnaryOp(T::Symbol, op::Symbol)
expressions = [ :($op(x.$field, x1.$field)) for field in names(eval(T))
]
body = Expr(:block, expressions...)
quote
function $op(x::$T, x1::$T)
$body
return x
end
end
end
function CompositeInPlaceBinaryOp(T::Symbol, op::Symbol)
expressions = [ :($op(x.$field, x1.$field, x2.$field)) for field in
names(eval(T)) ]
body = Expr(:block, expressions...)
quote
function $op(x::$T, x1::$T, x2::$T)
$body
return x
end
end
end
function CompositeIsEqual(T::Symbol, op::Symbol)
expressions = [ :($op(x1.$field, x2.$field)) for field in names(eval(T))
]
body = expressions[1]
for i = 2 : length(expressions)
body = Expr(:&&, body, expressions[i])
end
quote
function $op(x1::$T, x2::$T)
return $body
end
end
end
On Sunday, January 18, 2015 at 2:03:29 PM UTC+11, Andrew wrote:
> Thanks, lots of helpful stuff here. It's nice that this was being
> considered for inclusion by default. I will investigate not using abstract
> field types. I also like the idea to just use multiple subtypes, which
> would be good for organization especially if the model got more complex.
>
> Greg, I like your macro. It helped me understand metaprogramming some
> more. Also, the use of a function which defines the parameters explicitly
> is very similar to how I do this in MATLAB.
>
> I do have one question on your code for the macro. You write [ :($field)
> for field in names(eval(T)) ]. I played around with this and got
>
> In [225]:
>
> [ :($field) for field in names(eval(Parameters)) ]
>
> Out[225]:
>
> 6-element Array{Any,1}:
> :sigma
> :xi
> :eta
> :beta
> :rho
> :agrid
>
> In [226]:
>
> [ field for field in names(eval(Parameters)) ]
>
> Out[226]:
>
> 6-element Array{Any,1}:
> :sigma
> :xi
> :eta
> :beta
> :rho
> :agrid
>
>
> In [231]:
>
> names(eval(Parameters))
>
> Out[231]:
>
> 6-element Array{Symbol,1}:
> :sigma
> :xi
> :eta
> :beta
> :rho
> :agrid
>
>
> Is there a reason you went through the trouble of using a comprehension? I
> think just
> expressions = names(eval(T))
> does the same thing.
>
>
>
> On Saturday, January 17, 2015 at 6:08:42 PM UTC-5, Greg Plowman wrote:
>>
>>
>> Not sure if this is helpful.
>> Not sure if it is a good idea in general.
>> Certainly unsure if it is Julian.
>>
>> However, I find it useful because I can change fields of my type
>> (reorder, add, remove, rename etc) quite easily.
>>
>>
>> macro CallDefaultConstructor(T)
>> expressions = [ :($field) for field in names(eval(T)) ]
>> return Expr(:call, T, expressions...)
>> end
>>
>> type Parameters
>> sigma::Real
>> xi::Real
>> eta::Real
>> beta::Real
>> rho::Real
>> agrid::FloatRange
>> end
>>
>> function Parameters()
>> eta = 3
>> sigma = 1
>> rho = 5
>> xi = 2
>> agrid = linrange(1,10,10)
>> beta = 4
>>
>> @CallDefaultConstructor Parameters
>> end
>>
>> p = Parameters()
>>
>>
>>
>>
>>
>>
>> On Saturday, January 17, 2015 at 11:34:19 AM UTC+11, Andrew wrote:
>>
>>> Suppose I have a model which contains many parameters. I'd like to store
>>> my parameters in a type, for example
>>>
>>> type Parameters
>>> sigma::Real
>>> xi::Real
>>> eta::Real
>>> beta::Real
>>> rho::Real
>>> agrid::FloatRange
>>> end
>>>
>>>
>>> and then I need to assign some values to my parameters. The natural way
>>> I see to do this is
>>>
>>> params = Parameters(1,2,3,4,5,linrange(1,10,10))
>>>
>>>
>>>
>>> or something like that. However, the fact that I need to remember the
>>> order in which I defined these parameters means there is some chance of
>>> error. In reality I have about 20 parameters, so defining them this way
>>> would be quite annoying.
>>>
>>> It would be nice if there was a constructor that would let me use
>>> keyword arguments, as in
>>>
>>> params = Parameters(sigma=1,xi=2,eta=3,beta=4,rho=5,agrid=linrange(1,10,
>>> 10)) .
>>>
>>>
>>>
>>> I know I could write my own constructor and use keyword arguments, but
>>> then I think I'd still need to use the ordered constructor to write that
>>> one.
>>>
>>> Is there an easy way to do this? Maybe a macro that could automatically
>>> define a constructor with keyword arguments?(I don't know much about
>>> metaprogramming). Alternatively, is there is a cleaner way to store
>>> parameters that doesn't use types?
>>>
>>> ---
>>> I did find a related post here.
>>> https://groups.google.com/forum/#!searchin/julia-users/constructor$20keyword$20arguments/julia-users/xslxrihfO30/jV2awP5tbpEJ
>>>
>>> . Someone suggests that you can define a constructor like,
>>> Foo(;bar=1, baz=2) = new(bar, baz)
>>>
>>> which does what I want. Is there a way to macro that so that it's
>>> automatically defined for every field in the type?
>>>
>>