But wait – if you don't assign things back at the end, how is this different than just assigning to a local variable?
On Fri, Jun 13, 2014 at 7:07 PM, Stefan Karpinski <[email protected]> wrote: > Using a block seems like the right way to handle that. > > > On Fri, Jun 13, 2014 at 7:04 PM, Carlo Baldassi <[email protected]> > wrote: > >> No, that's the main annoyance admittedly, even though I find that most of >> the time I need to just read the values (or get a pointer to a mutable >> container which gets updated anyway). For that, one would either need to >> enclose everything in a block as in the @with macro and do some more magic >> (but this has problems of its own, and I'm particularly wary of unnecessary >> performance losses) or more simply have a mirror/sister macro to add at the >> end, I suppose. Up to now, I haven't really felt the need for that and >> therefore haven't implemented it. >> >> >> On Saturday, June 14, 2014 12:50:10 AM UTC+2, Stefan Karpinski wrote: >> >>> Does your @extract macro somehow assign values back to fields at the end >>> of the scope? >>> >>> >>> On Fri, Jun 13, 2014 at 5:29 PM, Carlo Baldassi <[email protected]> >>> wrote: >>> >>>> The point would be that it's not different, just more concise, so >>>> you're still in control of what you're doing and don't risk braking code >>>> when changing a type etc.; e.g. this is one typical function from some code >>>> I'm using: >>>> >>>> function foo(network::Network, i::Int) >>>> @extract network N H0 lambda state=current_state Ji=unsafe(J[i]) >>>> @extract state S s=unsafe(s) >>>> return dot(Ji, s) - H0 - lambda * (S - 0.5 * N) >>>> end >>>> >>>> which would be: >>>> >>>> function foo(network::Network, i::Int) >>>> N = network.N >>>> H0 = network.H0 >>>> lambda = network.lambda >>>> state = network.current_state >>>> Ji = unsafe(network.J[i]) >>>> S = state.S >>>> s = unsafe(state.s) >>>> >>>> return dot(Ji, s) - H0 - lambda * (S - 0.5 * N) >>>> end >>>> >>>> I just think the @extract version is clearer and more maintainable. >>>> Repeated across lots and lots of small functions, it means a consistent >>>> reduction in lines of code, and I think it allows to visually focus on the >>>> actual functionality rather than on the boring stuff. Also, adding more >>>> fields at the end of an @extract line is nicer when you change the fields >>>> of a type a lot (another typical situation I found myself into a lot is >>>> having a composite type which is just used to pass around tons of >>>> parameters, and having to extract them all whenever I use them). >>>> >>>> Anyway, so far I never actually thought of proposing inclusion into >>>> Base or even registering a package, but in the span of two days this thread >>>> came up and a coworker asked me for exactly this kind of thing (since he >>>> also was becoming annoyed by the long lists of assignments at the beginning >>>> of each function), so if someone likes the syntactic sugar the >>>> functionality is basically there. >>>> >>>> >>>> >>>> On Friday, June 13, 2014 10:45:44 PM UTC+2, Stefan Karpinski wrote: >>>> >>>>> How is this different than just assigning fields to local variables? >>>>> >>>>> On Friday, June 13, 2014, Carlo Baldassi <[email protected]> wrote: >>>>> >>>>>> Sorry for spamming, but after reading the discussion it seems like a >>>>>> (slightly polished) version of the @extract macro I mentioned above (I >>>>>> know >>>>>> the name is not great) already basically does that and a bit more, IIUC >>>>>> the >>>>>> discussion here. Some more examples and documentation are in the code at >>>>>> https://github.com/carlobaldassi/MacroUtils.jl/blob/master/ >>>>>> src/Extract.jl >>>>>> >>>>>> The nice thing (I think) about it is that if you have two objects of >>>>>> the same kind you can assign different names to the variables you use to >>>>>> refer to their fields, and even apply functions, e.g. say you have >>>>>> >>>>>> type Foo >>>>>> a::Int >>>>>> b::Vector{Float64} >>>>>> end >>>>>> >>>>>> you can do things like: >>>>>> >>>>>> function bar(f1::Foo, f2::Foo) >>>>>> @extract f1 a1=a b1=b >>>>>> @extract f2 a2=a b2=b >>>>>> >>>>>> # do stuff with a1,b1,a2,b2 instead of f1.a,f1.b,f2.a,f2.b >>>>>> end >>>>>> >>>>>> or even: >>>>>> >>>>>> function baz(f::Foo, i::Int) >>>>>> @extract f a bi=b[i] >>>>>> >>>>>> # do stuff with a and bi instead of f.a and f.b[i] >>>>>> >>>>>> @extract f b=unsafe(b) >>>>>> >>>>>> # do risky stuff with b >>>>>> end >>>>>> >>>>>> etc. (It may be even somewhat too fancy). >>>>>> >>>>>> Anyway, I happen to use that macro a lot in my code. >>>>>> >>>>>> >>>>>> >>>>>> On Friday, June 13, 2014 4:53:33 PM UTC+2, Mike Innes wrote: >>>>>>> >>>>>>> Absolutely – I didn't want to get into correct/incorrect when I >>>>>>> implemented @with but I definitely think the >>>>>>> >>>>>>> @with Foo::(a, b) >>>>>>> >>>>>>> syntax is preferable. I think I'll disable the type syntax, add >>>>>>> support for indexing and then send a PR to see if there's any chance of >>>>>>> having it in Base. >>>>>>> >>>>>>> (Personally I think the type syntax could be acceptable if used very >>>>>>> sparingly and only where it has significant benefit – e.g. for a very >>>>>>> large >>>>>>> config object that's used in many places. But I do agree that such a >>>>>>> construct wouldn't be best placed in Base.) >>>>>>> >>>>>>> >>>>>>> On 13 June 2014 15:25, Stefan Karpinski <[email protected]> >>>>>>> wrote: >>>>>>> >>>>>>>> Keno's example showed how a simple error like forgetting that you >>>>>>>> had assigned to `a` would cause problems, but it's even worse – that's >>>>>>>> just >>>>>>>> a matter of making an error about the current state of the program. >>>>>>>> It's >>>>>>>> worse than that though: if someone adds a field to a type that is used >>>>>>>> *anywhere* with such a `using` construct, that code becomes incorrect. >>>>>>>> The >>>>>>>> fundamental problem with this construct is that it makes it locally >>>>>>>> impossible to reason about what it means when you access or assign a >>>>>>>> binding. Let's say I have this code: >>>>>>>> >>>>>>>> function foo(x::Bar) >>>>>>>> using x >>>>>>>> a = b + 1 >>>>>>>> >>>>>>>> end >>>>>>>> >>>>>>>> >>>>>>>> What does this mean? b could be a global variable or a field of x; >>>>>>>> a could be a local variable or a field of x. Without knowing the >>>>>>>> structure >>>>>>>> of Bar, we can't know. In Julia, we never does this: you can always >>>>>>>> tell >>>>>>>> the meaning of code from purely local syntactic analysis. The code may >>>>>>>> be >>>>>>>> wrong – you might try to access or assign a field that doesn't exist, >>>>>>>> but >>>>>>>> there is only one thing your code could mean. For the same exact >>>>>>>> reasons, I >>>>>>>> wouldn't accept a macro that simulates this into base – it has the >>>>>>>> exact >>>>>>>> same problems. The only way to make this acceptable is if can be >>>>>>>> locally >>>>>>>> disambiguated what the code means. You could, for example, do something >>>>>>>> like this: >>>>>>>> >>>>>>>> function foo(x::Bar) >>>>>>>> >>>>>>>> using x: a, b >>>>>>>> >>>>>>>> a = b + 1 >>>>>>>> >>>>>>>> end >>>>>>>> >>>>>>>> >>>>>>>> Now you can immediately tell that `a` and `b` are both fields of >>>>>>>> `x` and not global or local variables (not by accident, this is >>>>>>>> independent >>>>>>>> of the definition of Bar). >>>>>>>> >>>>>>>> >>>>>>>> On Thu, Jun 12, 2014 at 7:40 AM, Jameson Nash <[email protected]> >>>>>>>> wrote: >>>>>>>> >>>>>>>>> > Having the local keyword like it is makes most sense to me, but >>>>>>>>> I suppose it isn't a big deal to me that if you don't explicitly >>>>>>>>> specify >>>>>>>>> local you could be referring to something outside the current scope, >>>>>>>>> which >>>>>>>>> is the case with for loops. >>>>>>>>> >>>>>>>>> Javascript does this. It also has the "using" block that you >>>>>>>>> describe (see "with"). They are probably the worst (mis)features in >>>>>>>>> the >>>>>>>>> entire language. In the latest version of javascript, it has finally >>>>>>>>> been >>>>>>>>> removed, to the relief of javascript programmers everywhere: >>>>>>>>> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ >>>>>>>>> Functions_and_function_scope/Strict_mode. >>>>>>>>> >>>>>>>>> I would be interested in having a language feature akin to visual >>>>>>>>> basic's with block, since it is only a trivial source transform to >>>>>>>>> annotate >>>>>>>>> each `.` with the result of the expression in the nearest `with` >>>>>>>>> block. >>>>>>>>> function f(A) >>>>>>>>> with A >>>>>>>>> .c = .a + .b >>>>>>>>> end >>>>>>>>> end >>>>>>>>> However, since this only saves a one character, it really isn't >>>>>>>>> worthwhile. >>>>>>>>> function f(A) >>>>>>>>> Z = A >>>>>>>>> Z.c = Z.a + Z.b >>>>>>>>> end >>>>>>>>> >>>>>>>>> >>>>>>>>> On Thu, Jun 12, 2014 at 6:14 AM, Mike Innes <[email protected]> >>>>>>>>> wrote: >>>>>>>>> >>>>>>>>> Ok, managed to have a quick go at this – source with some examples: >>>>>>>>> >>>>>>>>> https://gist.github.com/one-more-minute/668c5c7cdd8fd8b81d35 >>>>>>>>> >>>>>>>>> Currently it does nothing to avoid the issue Keno pointed out, but >>>>>>>>> in principle you could throw an error when the mutating version is >>>>>>>>> used >>>>>>>>> without explicit types. >>>>>>>>> >>>>>>>>> If there's any interest in having this in Base you're welcome to >>>>>>>>> it, otherwise I'll probably just clean it up and store it in Lazy.jl. >>>>>>>>> >>>>>>>>> >>>>>>>>> On Thursday, 12 June 2014 09:44:30 UTC+1, Andrew Simper wrote: >>>>>>>>> >>>>>>>>> Brilliant Mike! This is exactly what I was after, I just want a >>>>>>>>> way to write shorthand names for things within a scope, and the @with >>>>>>>>> macro >>>>>>>>> does just that :) In the example I posted I split the coefficients >>>>>>>>> away >>>>>>>>> from the state so that only the state needs to be returned, I think >>>>>>>>> this is >>>>>>>>> good for efficiency. I'll have a play with @with and see how I go. >>>>>>>>> Passing >>>>>>>>> in names (Typename), isn't a problem, since when a new name is added >>>>>>>>> there >>>>>>>>> is no duplication in doing this. >>>>>>>>> >>>>>>>>> Keno, sorry for not understanding that this is probably what you >>>>>>>>> meant when you said this would be best off done by using macros, I >>>>>>>>> didn't >>>>>>>>> think of enclosing the entire algorithm in a macro. >>>>>>>>> >>>>>>>>> On Thursday, June 12, 2014 4:21:58 PM UTC+8, Mike Innes wrote: >>>>>>>>> >>>>>>>>> FWIW – putting to one side the question of whether or not this is >>>>>>>>> a good idea – it would be possible to do this without new language >>>>>>>>> syntax. >>>>>>>>> However, you'd have to either pass a type hint or be explicit about >>>>>>>>> the >>>>>>>>> variables you want: >>>>>>>>> >>>>>>>>> e.g. >>>>>>>>> >>>>>>>>> function tick(state::SvfSinOsc, coef::SvfSinOscCoef) >>>>>>>>> @with state::SvfSinOsc, coef::SvfSinOsc >>>>>>>>> # or >>>>>>>>> @with state (ic1eq, ic2eq) coef (g0, g1) >>>>>>>>> >>>>>>>>> lv1 = g0*ic1eq - g1*ic2eq >>>>>>>>> lv2 = g1*ic1eq + g0*ic2eq >>>>>>>>> SvfSinOsc(2*v1 - ic1eq, 2*v2 - ic2eq) >>>>>>>>> end >>>>>>>>> >>>>>>>>> This would work in the non-mutating case by calling names() on the >>>>>>>>> type and making appropriate variable declarations. >>>>>>>>> >>>>>>>>> You could then go further and implement >>>>>>>>> >>>>>>>>> function tick(state::SvfSinOsc, coef::SvfSinOscCoef) >>>>>>>>> @with state (ic1eq, ic2eq) coef (g0, g1) begin >>>>>>>>> >>>>>>>>> lv1 = g0*ic1eq - g1*ic2eq >>>>>>>>> lv2 = g1*ic1eq + g0*ic2eq >>>>>>>>> SvfSinOsc(2*v1 - ic1eq, 2*v2 - ic2eq) >>>>>>>>> end >>>>>>>>> end >>>>>>>>> >>>>>>>>> Which would walk over the expression, replacing `a` with `Foo.a`. >>>>>>>>> However, it would be tricky to implement this correctly since you'd >>>>>>>>> have to >>>>>>>>> be aware of variable scoping within the expression. >>>>>>>>> >>>>>>>>> I may implement the non-mutating version of this at some point – >>>>>>>>> it seems like it could be useful. >>>>>>>>> >>>>>>>>> >>>>>>>>> On Thursday, 12 June 2014 08:21:42 UTC+1, Andrew Simper wrote: >>>>>>>>> >>>>>>>>> On Thursday, June 12, 2014 2:16:30 PM UTC+8, Andrew Simper wrote: >>>>>>>>> >>>>>>>>> It seems that the local keyword is a bit of a language kludge to >>>>>>>>> me, since it is implie >>>>>>>>> >>>>>>>>> >>>>>>>> >>>>>>> >>> >
