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