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?
>>>
>>

Reply via email to