Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

method invocation using . #92

Closed
mattaylor opened this issue Mar 31, 2013 · 13 comments
Closed

method invocation using . #92

mattaylor opened this issue Mar 31, 2013 · 13 comments

Comments

@mattaylor
Copy link

I know this issue has been discussed a couple of times before and closed, but Id really like to understand why the . operator could not be used to invoke method in additon to than the decidedly ugly \ (which apart from being ugly is also highly confusing when not used as an escape character.

The exisitng \ could still be retained for back- compatability, but it should also be possible to support . notation as well. If the RHS is trailed with arguments, () or ! then it should be easy to determine that this is method invocation rather than property access. I can't believe that there is much of a penalty for this kind of linguistic simplification, which is afterall one of the chief reasons for using a language like moonscript in the first place.

This would not only improve code readability but it would also align so much better with many of the most popuar languages out there including java, python, javascript, ruby etc...
Which could help adoption. This is probably the single biggest barrier to cooffescript compatible moonscript and vice versa.

@fillest
Copy link

fillest commented Mar 31, 2013

(Hm, maybe it would be better to continue discussion in the existing thread, not to create a new one..)

Ah, backward compatibility is a good idea.
But I think the syntax should be :, not .. There should be a way to call a method like in Lua with ., and with backward compatibility it couldn't be \ and should be backward compatible too. And : would be more compatible with Lua (which is obviously much more important than coffee-compatibility).

I see no ambiguity with e.g. hash-: because one really should use whitespace after : in hashes anyway (if I would make a language, I would made it mandatory :)). Maybe it would complicate parsing but it's worth it. I personally got used to \ thing after some time but it's still non-smooth to type because of "fingers memory", and it looks odd.

@leafo
Copy link
Owner

leafo commented Mar 31, 2013

I've already discussed this here: #35 (comment)

Do you understand why Lua has both . and : operators? That's why I can't use . for both access and invocation. They are two separate operations that are used in different circumstances. I could force all function calls on an object to pass the receiver (dot everywhere) but then I would lose compatibility with a lot of Lua code (or at least make it very frustrating).

I describe in detail in the other issue why it's important to have two separate operators for method invocation, table property assignment and regular assignment. (Thus I don't use : like in Lua)

\ is not highly confusing with the escape character because escape sequences can only appear inside of strings. (maybe confusing if you embed code into strings, but I think it's highly more likely that you will use the [[ ]] string delimiters for embedding code and there are no escape sequences there)

Additionally, I'm not looking to encourage running CoffeeScript inside of MoonScript. They are too different to make it worth it, and in my opinion Lua got a lot of things right that are completely broken with JavaScript. If I'm spending my time trying to convert Lua into something that works like JavaScript then I've got it all wrong.

@mattaylor
Copy link
Author

I understand your arguments, however the compromise proposal from zah in #35 (comment) seems like a very elegent solution that could address your concerns whilst also providing a more accessible syntax. Apart from the fact that these woud be breaking, backwards incompatible changes, are there any other reasons why not to go down this route?

@etandel
Copy link

etandel commented Apr 1, 2013

This is probably the single biggest barrier to cooffescript compatible moonscript and vice versa.

Well, this and the very different underlying semantics of MS (Lua) and CS (JavaScript).

the compromise proposal from zah in #35 seems like a very elegent solution (...). Apart from the fact that these woud be breaking, backwards incompatible changes, are there any other reasons why not to go down this route?

Having . in MS mean the same as : in Lua would be, IMHO, very confusing, since MoonScript's semantics are highly dependent on Lua's.

BTW, I have nothing against \. Sure, it's different at first, but it's simple and straightforward (just use it when you'd use : in Lua); I don't see why anything else is better. Well, except for making it look kinda but not the same as a bunch of other languages.

Also, backwards compatibility is a a big thing for such a trivial and subjective problem (which token looks nicer).

@exists-forall
Copy link

I think the \ operator is fine, but if you don't like it, go right ahead and only use . All it requires is a slightly different approach to your definition of an object.

If you haven't already, the closures and objects are equivalent article on the C2 wiki is definitely worth a read. While a little disjointed (as such C2 articles always are), the point is clear and profound: having either closures or objects in a language, especially a dynamic one (whatever that really means), is enough to gain the benefits of both. This is because a closure, like an object, is a binding of (in Lua, mutable) data to runtime-determined behavior. With this, you get two of the major selling points for object-orientation: data hiding and polymorphism. The only caveat is that a closure can only specify one method, but this is trivially easy to solve by combining multiple closures into a table:

new_account = ->
    with {}
        balance = 0
        .get_balance = -> balance
        .deposit = (amount)-> balance += amount
        .withdraw = (amount)->
            balance -= amount
            assert balance >= 0, "You're broke!"
            amount

and there you have it: a full-featured object, complete with encapsulation (which is actually more than the standard class construct can offer you)!

This is relavant because, under this scheme, the convention of passing in a "self" argument (which is redundant and unsafe, actually, as a malicious user could potentially set "self" to anything they want) becomes a thing of the past, eliminating the need for a \ operator entirely (except for interfacing with code that does not use this convention, such as Love2D's images). Our account can be used like so:

my_account = new_account!
my_account.deposit 100
buy_something_at_starbucks my_account.withdraw 5

Look ma, no \! This is just like in Python, except in Python the creation of the closure is done when the method is requested, not when the object is created. This is less efficient, but it's a late biding and thus yields more flexibility: methods can be added and modified to classes at runtime and that modification will propagate to all living objects. The same cannot be said of our implementation of Account, however: our entire "class" is a function, and therefore a black box, not subject to inspection, manipulation, etc.

If you want more python-like classes that still use ., you can write a function that creates them:

pythonic_class = =>
    result = self
    instance_mt = {
        __index: (key)=>
            if 'function' == type result.methods[key]
                (...)-> result[key] self, ...
            else
                result[key]
        -- maybe add other metamethods whose
        -- implementations delegate their computation
        -- to instance-defined functions.  For example: __add: (other)=> self.__add other
    }
    uninformative_error_message_if_the_client_is_trying_to_do_something_stupid = [[
Dangerous voodoo!  If you're getting this error message, that probably wan't very informative, but that's ok: you know what you did.  Stop messing with "self" and clean up any other code-smell like this!
]]
    setmetatable self, {
        __call: (...)=>
            assert rawequal(self, result), uninformative_error_message_if_the_client_is_trying_to_do_something_stupid
            instance = setmetatable {}, instance_mt
            instance.__init ... -- in the spirit of python.  Rename to "new" if you so please.
        __index: (key)=>
            assert rawequal(self, result), uninformative_error_message_if_the_client_is_trying_to_do_something_stupid
            @parent and @parent[key]
    }

use it like this:

Account = pythonic_class
    __init: (@balance)=>
    deposit: (amount)=> @balance += amount
    withdraw: (amount)=>
        @balance -= amount
        assert @balance >= 0, "You're broke!"
        amount

my_account = Account 100
buy_something_at_starbucks my_account.withdraw 5
-- note: since this is a close-emulation of python semantics, we also get the ability to directly modify fields
-- that is, there is no data hiding.  Watch:
my_account.balance = "Where is your encapsulation god now?"
-- still, that's standard moonscript semantics, so you're probably already comfortable with it.

the neat thing is that here, even though methods are closures, the implementation of those closures is subject to change:

-- all the above code...
old_deposit = Account.deposit
Account.deposit = (amount)=>
    -- check for sneaky negative amounts that could put you in debt without issuing the correct error message
    assert amount >= 0
    old_deposit self, amount

This is much closer to the way that moonscript classes work. Really the only difference is that you can use your arguably more readable . syntax, with no modification to the language!

Although javascript and ruby both appear to work using the system outlined above as well, there are subtle distinctions: In Javascript and ruby, . doesn't create a closure, but instead behaves more like lua's :. Even functions that seem to have no receiver, like library functions or global functions, implicitly receive their owner even when it goes unused. This also means that . cannot be used to create callbacks in these languages. For these reasons, I've been talking specifically about python, not just "dot-notation languages" as a whole.

Important note: Be wary of the member-function-call syntax. This:

@foo bar, baz

compiles to this:

self:foo(bar, baz)

which will cause a subtle error if self is a dot-notation object. Use:

@.foo bar, baz

instead.

@stevedonovan
Copy link

I'm with Leaf on this one - the two operators are distinct in Lua for a good reason (it's one of the reasons why Lua is so much more zippy than Python, which has to create this closure implicitly). \ is a bit ugly sure, but colon will not do, alas. Mr Simian shows us how one can explicitly construct objects that preserve '.' by making all these closures up front.

Compatibility with the rest of the Lua ecosystem is important, because otherwise MS is a cool language without libraries ;)

MS has a nice bit of sugar that f\foo without a call operator implicitly creates a closure - another nice feature we've been wanting in Lua land for some time.

@fillest
Copy link

fillest commented Apr 1, 2013

Why not : really? :)

@mattaylor
Copy link
Author

@SelectricSimian very helpfull discussion. Thanks,

@leafo
Copy link
Owner

leafo commented Apr 1, 2013

@fillest I want to allow line breaks after access operators:

x = some_object\
    a_method!

If the : was used in both places then it would look like a table literal. I think it's a lot simpler from a grammar perspective and the programmer's perspective if they are different characters.

@fillest
Copy link

fillest commented Apr 1, 2013

@leafo After? Hm, it's better to place it before:

x = obj
    \some!
    \method!
    \chain!

vs

x = obj\
    some!\
    method!\
    chain!

But ah yes, it would really look like tables, ok sorry..

x = obj
    :some!
    :method!
    :chain!

@leafo
Copy link
Owner

leafo commented Apr 1, 2013

@fillest It conflicts with the syntax of with

with something
  \hello!

@eloff
Copy link
Contributor

eloff commented Apr 2, 2013

I thought \ was really ugly when I started using it, but maybe that's just because it's so different from other languages. I think it's fine now, it's as good as any other character, and easy to type (no shift key) unlike :

It looks a little like an arrow from the instance to the method.

@exists-forall
Copy link

I agree. I think \ looks fine once you get used to it. Less detail than :, actually: just a simple straight line, rather than two little dots you have to strain to see (or distinguish from .). Easier on the eyes once you let go of your existing biases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants