Re: Keyword Arguments
Steve Dekorte <[email protected]> Thu, 21 Jun 2012 00:10:32 -0700
| Newsgroups | gmane.comp.lang.io |
|---|---|
| Message-ID | <[email protected]> |
On 2012-06-16 Sat, at 02:33 PM, Kevin Edwards wrote: > >> Example: >> >> aPerson setAge(thePersonsAge) >> aPerson print >> aPerson canRetire >> >> vs. >> >> aPerson print(thePersonsAge) >> aPerson canRetire(thePersonsAge) >> >> etc >> >> It's clear that the age is being stored in the wrong place and that passing it in constantly is a bad design choice. >> To me this suggests that anything that encourages developers to pass more data also encourages them to make bad design decisions. > > I agree with you entirely here, I'm just saying that keyword argument syntax could do the exact same thing as your first example. e.g.: > > aPerson = Person(age = thePersonsAge) > aPerson print > aPerson canRetire > > Note that the same age setter would be used as in your example. You wouldn't need to implement a monolithic constructor to handle that -- it would be modular, as in your example. > > To be clear, I'm not advocating the traditional implementation of keyword args. Instead, I'm suggesting that it would roughly correspond to `Person clone do( age = thePersonsAge ) eval` because that is basically what an eager method call does -- it clones the definition, binds the arguments, and returns the result. The syntax is just a natural expression of the relationships involved. The problem is that the keyword arg doesn't save any code, adds new syntax and semantics, and encourages more argument passing, which (it seems we are coming to agree) is typically a sign of bad design. > >> Also, you might look at how self unified assignment with methods. Maybe there is something there. > > Could you elaborate? I thought Self did roughly the same thing as Io, but calls it `x:` rather than `setX`. Io actually adds setter methods. I'm not sure if Self does that. >>> i think part of the answer to performance is a more flexible dispatcher which can take the `at` and `put` together, so that an explicit Slot object doesn't actually have to be created if it doesn't already exist. But this may bear some similarity to multiple dispatch, so I'm interested in learning your thoughts about that anti-pattern. :-) >> >> Multiple dispatch makes the deep assumption that a notion of external type exists. But what notion of type can deal with something like a proxy which is, in effect, all possible types? > > Proxies have a distinct type that is defined at a meta-level relative to the proxied type. For example, a perfectly transparent Proxy will respond to all messages identically to how the proxied type would respond, and an external ProxyViewer object would be used to look inside the Proxy and control the Proxy's underlying behavior. > > We use proxies specifically for this special meta-behavior which defines the Proxy type distinctly from other types. If you don't go to the meta-level to define Proxy, and instead try to merge that meta-behavior into the base behavior, you end up with an imperfect Proxy. You're still making assumptions about what is on the other side of the proxy. :) For example, Io's futures are proxies but to objects that may not exist yet. So what "type" is the proxy in that case? If it's a type that responds to everything, then multiple dispatch breaks. Likewise, the proxy may wrap something loaded or created at runtime or discovered across a network, etc and/or it may be an object which changes at runtime. All these possibilities break multiple-dispatch. > >> Or from another angle: any system that depends on a notion of type that is anything more than asking an object what it choses to do at the moment violates encapsulation in ways that have serious consequences for what the system can and can't do. > > That is a fascinating and profound angle. :-) > > You're right that the notion of type imposes constraints on what an object can and can't do, but that can actually be very useful because it explicitly tells us the bounds within which the object can change while maintaining a coherent system. Granted, a lot of type systems unnecessarily over-constrain objects or prohibit changing constraints at run-time, but that does not mean it has to be that way. The system I want to use is one where there is no such thing as non-runtime (no compile time) and anything that prevents changes that can occur at runtime (sans those that can't be caught as runtime exceptions) is AFAICS incompatible with this. > For example, we can instrument an `if(<cond>, <then>)` call and change its implementation in all sorts of flexible ways, but fundamentally it must always evaluate <then> based upon the condition. I've actually implemented useful code that broke this assumption. :) The case was a JIT compiler binding for Io using libJIT. I defined if(), etc as methods that acted on the jitting function object so you would just run the Io code to JIT the method. By reusing Io's evaluation, it avoided having to write an AST walker and a bunch of related code to do the compilation. Steve