Open eloff opened 11 years ago
Seems like a useful feature, but runs into the same subtle issue mentioned when discussing new
returning a new self
(I had no idea that inline code looked so cool in a link!). The problem is that implicit return values can cause you to override the class without meaning to.
Good point. Both cases could be solved by requiring a slightly more complex return value before overriding the class/self. E.g. return (value, True) or {replace: value} or {self: value} etc. Then it would be very hard to do by accident.
A "cleaner" solution would be to allow replacing self/class with some kind of assignment from within the method, but I'm not sure if that can be easily done without special help from the parser.
I like the idea of assignment from within the method. To do this we'd need some sort of pass-by-reference semantics, but that's easily accomplished with a table, which is implicitly a reference type. In fact, we don't even need to add a second (or third, if you're contain self
) parameter to __inherited - we can just use the table that's already being passed in by detecting if it's had a certain field set:
if _parent_0 and _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
if _class_0.__override then
_class_0 = _class_0.__override
end
end
pass-by-reference should have a more general support in the language, however. I'm starting up a new issue right now.
I like that solution, it's clean and simple.
On Tue, Apr 2, 2013 at 11:33 PM, SelectricSimian notifications@github.comwrote:
I like the idea of assignment from within the method. To do this we'd need some sort of pass-by-reference semantics, but that's easily accomplished with a table, which is implicitly a reference type. In fact, we don't even need to add a second (or third, if you're contain self) parameter to __inherited - we can just use the table that's already being passed in by detecting if it's had a certain field set:
if _parent_0 and _parent_0.inherited then _parent_0.inherited(_parent_0, _class_0) if _class_0.override then _class_0 = _class_0.override endend
pass-by-reference should have a more general support in the language, however. I'm starting up a new issue right now.
— Reply to this email directly or view it on GitHubhttps://github.com/leafo/moonscript/issues/93#issuecomment-15817506 .
Actually, I was going to start an issue about pass-by-reference, behaving similarly to C++ reference params, such as
void swap(int& a, int& b) {
int temp = b;
b = a;
a = temp;
}
but then realized that most use cases were just to provide a work-around in the absence of multiple return values. All the other use cases were just to prevent an assignment. Things like
void drop(immutableLinkedListRepresentingStack& stk) {
stk = stk.next;
}
which could theoretically be useful in moonscript too
drop = (stk_ref_of_some_sort)->
stk_ref_of_some_sort\set stk_ref_of_some_sort\get!.next
used like this
drop ^my_stack
-- or possibly
drop &my_stack
-- or
drop `my_stack
however, this is made infinitely clearer by the (slightly) more verbose, cutting edge technology of return values!
my_stack = drop my_stack
maybe what we need is something like Arc's zap
macro (do any other lisps have this?), but as a language feature. How about a zap-with
construct (keyword reuse: resourceful or unreadable?), to generalize update assignment
zap my_stack with drop
extra operands in the zap-with
syntax could by compiling code like this
zap foo with bar baz, qux
into this
foo = bar(foo, baz, qux)
although this is ambiguous with
foo = bar(baz, qux)(foo)
since every function is just an expression.
Maybe a more classic update assignment is in order
my_stack drop= -- pretty weird not having anything on this side of the equals sign...
this looks a lot better when you have another operand, which is the case with all the existing assignment operators
my_stack push= io.read! -- this is better
although, this would probably just be done with an overloaded ..
my_stack ..= io.read! -- simpler?
thoughts?
Lisp also has a form of pass-by-reference, actually, which is macros, since a macro can insert an assignment into the generated code. However, lisp references come with an extra bonus feature that could be useful for moonscript libraries, which is the ability to see the name of the reference. For example, a Lisp class system might use something like this
(class Hello
(defmethod (whatever x y z)
foo)
(defmethod (something a b c)
bar))
with the class
macro using Hello
both as the symbol to assign the new class too, and a field inside the class, so if we created an instance
(set my-hello (new Hello))
(print (send my-hello get-class-name))
it would know the name of its class. This is like what moonscript already does in its class construct by assigning to __name
as well as using that string as the name of the variable to put the class in. This is because Moonscript is sort of an ad-hoc set of macros implemented for Lua. However, it would be useful to give everyone the ability to write these name-grabbing functions, and not just limit them to being language-level. For example, if I'm using love2D, I have to write the name of every image twice in my code
player_running_right_1 = love.graphics.newImage "player_running_right_1.png"
player_running_right_2 = love.graphics.newImage "player_running_right_2.png"
-- etc...
however, in a lispy language I could write a load-image
macro and use it like so
(load-image player-running-right-1)
(load-image player-running-right-2)
we don't have to have lisp envy, though. There's no reason you need macros to accomplish any of these tasks - they're just how they've been implemented in the past.
Imagine a (unary) ^
operator (following the same ambiguity rules as unary minus), where
^foo
compiles to
{
get = function()
return foo
end;
set = function(newFoo)
foo = newFoo
return foo
end;
name = "foo";
}
now we can write our load_image
function like this
load_image = (img_ref)->
img_ref.set love.graphics.newImage img_ref.name .. ".png"
and use it like this
load_image ^player_running_right_1
load_image ^player_running_right_2
-- etc...
I can think of maybe 10 or so other times I've wanted this in the past, mostly for implementing my own class systems though. Useful?
This unary ^
operator would also be useful for self-referential immutable data structures. I was writing a PEG (parsing expression grammar, which happens to be what moonscript uses for all of its parsing) library the other day, and I kept running into the issue of my Pattern
objects, being immutable, not being able to be initialized with themselves as parameters. I eventually settled on using closures, like this
nested_parens = pattern.TwoPossibilityPattern (pattern.CompoundPattern '(', pattern.LazyPattern(->nested_parens), ')'), '()'
however, this seems like a good use for the get
field of references
nested_parens = pattern.TwoPossibilityPattern (pattern.CompoundPattern '(', pattern.LazyPattern(^nested_parens), ')'), '()'
It literally only saves one character, but I think it makes the intent clearer.
</incoherent-rant belongs-on-this-issue=false>
Just one more potential use case! Properties in closure-based object systems. For example, if we have a closure-based class like mentioned here, it's common to have to write accessor methods (which are far better than raw fields, because they're polymorphic), like this
Person = (name)->
with {}
.get_name = -> name
.set_name = (new_name)-> name = new_name
-- various other methods...
^
is perfect here. Consider an Object
base class
Object = ->
with self = {}
.property = (ref)->
self["get_" .. ref.name] = ref.get
self["set_" .. ref.name] = ref.set
and a rewritten Person
Person = (name)->
with Object!
.property ^name
-- various other methods
I'm going to work up a PR soon for the solution suggested by SelectricSimian. If there's any objections or alternative proposals please let me know.
I overloaded __inherited, but what I really want to do is return a new class from the method and have that replace the calling child class.
It's easily done with full backward compatibility with a minor tweak to the code gen. Replace:
with
Thoughts?