Re: Re: Keyword Arguments
Kevin Edwards <[email protected]> Tue, 18 Sep 2012 18:40:43 -0500
| Newsgroups | gmane.comp.lang.io |
|---|---|
| Message-ID | <[email protected]> |
On 9/17/2012 12:28 AM, dennisf486 wrote:
> But before that I want to throw in a comment about all this talk of perfect vs. imperfect proxies: I think what you want here is Gilaad Bracha's mirrors. The idea is just that by requiring all meta operations to go through a mirror object constructed from the target object, you side-step questions like where methods like "forward" should go on a proxy because the metaobject api is no longer part of the interface of "Object". Then it would be a non-issue.
Exactly. Merging proxy management functions into the same namespace
as the target object corrupts the view. It conflates two different
denotations -- the object through the proxy and the proxy itself.
> Now, multiple dispatch: my use of it is perhaps the exception that proves the rule in support of Steve's argument. You see, I needed multiple dispatch precisely for the situations in which encapsulation had to be broken. What I did was I took all the icky code I had in the engine that knew about the internal details of two different kinds of objects, and encapsulated it into a third location which would be called via a multiple dispatch when those two kinds of objects interact.
I'd probably understand better with more specific examples, but that
doesn't sound too exceptional to me, and your solution of factoring
out the common logic sounds pretty standard as far as I understand it.
> Anyhow just as Steve said, it immediately tied me to certain a priori assumptions about types of objects because a) I had to assign type labels to objects to make it work
From my perspective, type labels are fine as a summary of
constraints, but problems can occur in more complex systems if those
constraints are ill-defined or when you are restricted to only a
single type label and there's no decomposition and no management of
equivalence between type labels.
> and b) I was never able to figure out a way to make it conform more closely to the Io spirit of dynamic typing where you completely allow target objects to choose their own way to respond. (The difficulty with having multiple dispatch while still allowing objects to completely choose their response is because of the chicken and egg problem that object A's response must depend on object B's whose response must depend on object A's and so on.)
Yeah, the combined logic has to actually exist somewhere; the
routing can't just loop forever. :) A and B could still have single
dispatch stubs which route to your common logic. And ideally, a
multiple dispatch system would automatically generate those for you
as part of the decision tree.
I don't know your requirements or how your Interaction objects
actually work, but I'll just mention that another way to model the
domain is for objects to send out packets, each representing an
abstracted stimulus, analogous to how physical objects send out
gravitons, photons, etc. in quantum mechanics. So, an interaction
would be the result of A sending packets to B and B sending packets
to A, and each object responding to those packets as it sees fit.
This doesn't really get rid of multiple dispatch, but it might help
you organize your interactions.
> Anyway to quickly wrap up the other points I brought up -
>
> Steve saying he wants to eliminate compile time so that everything is run time is interesting to me because in the language I'm working on now, I plan to use staged compilation to achieve a similar the goal of having the meta language be just the same as the implementation language; but instead of eliminating compiling I just plan to run the compiler recursively. My point being that I think you can go left or right here and end up in a similar end state.
Yeah, Steve was probably talking about eliminating "compile time"
and making it incremental at run-time, not eliminating it altogether.
> For the language I'm working on I plan to use ":" to mean "having type of" so that
> a : Int
> creates a label (or variable) "a" having type "Int". The kicker is the thing on the "type" side can be any expression including function calls, so that you can write functions that compute types. The problem I have now is that complex expressions involving ":" are ambiguous as to grouping. My point is it's possible ":" is just a poor choice for many kinds of syntax because parens or brackets are much more unambiguous. (For instance Powershell puts types in [square brackets]. It looks dorky but it's clear.) I may use ":" syntax anyway though because it is easier to type.
You could, of course, use parens to explicitly control grouping when
necessary. Setting the right precedence could help, too, so that
":" would be terminated by a comma or semi-colon or new line, etc.
When thinking about syntax, it helps me to go through specific
examples and actual code, because it can be hard to guess what the
most common cases are that you want to optimize for (e.g. how often
and in what contexts do you want to inline type computations).
I also usually favor reading over writing syntax, though they often
dovetail. Too many brackets can distract reading, but so can too
few if it takes longer to mentally parse.
> Finally, to get back to keyword arguments - I'm really conflicted as to whether to use named arguments or positional ones for the language I'm creating, or perhaps allow both. I don't think named arguments would work for Io because "f(x:=1, y:=2)" would only work if you messed with Io's scope rules which would be a pretty drastic change.
I'm not sure how drastic it would be. I'd imagine that the only
time you really see "f(x := 1)" is when the param is lazy, which
could have some special handling. Otherwise, it's probably
considered bad practice to send setSlot("x", 1) to the outer locals
from an arg, as it is in most languages.
> The way I plan to make it work in the language I'm designing is to make curly brace blocks define scopes and make structs, tuples, and argument lists all interchangeable & decompose to each other. So, "f( { x:=1, y:=2 } )" would not leak "x" and "y" (because the brace defines a scope, the "{ whatever }" would decompose to a "named tuple" which is the same thing as a struct (i.e. a tuple where the positions have names) and then the struct would in turn splat as the arg list for "f()". Actually, it might be possible to define the braces operator in Io too to enable something like this, if you were dead set on using named arguments?
Yeah, passing a struct or dict is how some languages suggest binding
by name, but dealing with that extra packaging distracts from the
dev's intent and you have to remember when to use it and when not to
use it, so I don't think it's common. And I want to reduce
packaging concerns, not increase them.
But do you mean that the struct is automatically splat? If so, I
don't understand what you are gaining from the inner {}, unless you
want "f( x:=1, y:=2 )" to have a different meaning? As I said, I
don't think setting outer locals from args is that common.
I also think there are more benefits to targeting a scope per
reference rather than creating more scopes. e.g. Given "f( ^x:=1,
y:=2 )", x would be set in the outer scope and bound positionally to
arg0, while y would be set in the cloned f's scope.
Anyhow, I wasn't sure what to respond to, so I've just sprinkled
some thoughts throughout. I hope they're helpful. :)
Good luck on your project!
Kevin