Navigation Menu

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

Allow calling functions with a bang! #2289

Closed
wants to merge 1 commit into from
Closed

Conversation

qrush
Copy link

@qrush qrush commented Apr 26, 2012

This commit adds a little loophole into the rewriter to allow syntax like:

class Foo
  bar: ->
    "Bang!"

faz = new Foo!
faz.bar! # Bang!

I think it makes extending into objects when you need to use functions a
lot cleaner:

Number.prototype.seconds = ->
   this * 1000

4.seconds! # 4000

Perhaps it's a case of parens-itis. Or perhaps it's awesome! I
think it'll end up making quick little calls to methods a little less
painful. One character less.

This was originally inspired by @joshuaclayton's tweet:

https://twitter.com/#!/joshuaclayton/statuses/190496586533576705

This commit adds a little loophole into the rewriter to allow syntax like:

    class Foo
      bar: ->
        "Bang!"

    faz = new Foo!
    faz.bar! # Bang!

I think it makes extending into objects when you need to use functions a
lot cleaner:

    Number.prototype.seconds = ->
       this * 1000

    4.seconds! # 4000

Perhaps it's a case of parens-itis. Or perhaps it's awesome! I
think it'll end up making quick little calls to methods a little less
painful. One character less.

This was originally inspired by @joshuaclayton's tweet:

https://twitter.com/#!/joshuaclayton/statuses/190496586533576705
@soffes
Copy link

soffes commented Apr 26, 2012

Totally love it!

@gmcintire
Copy link

That's pretty handy, definitely +1

@TrevorBurnham
Copy link
Collaborator

See #514, particular's Jeremy's comment when closing it: #514 (comment)

@plukevdh
Copy link

Hmm. More ruby-like, but imo makes less sense. Might be a point of confusion when people come to coffeescript and wonder why they cant just call faz.bar

In that case faz.bar() makes more sense to me than faz.bar!

Embrace the parens, just like you did significant indentation levels. :trollface:

@ngauthier
Copy link

or allow calling methods with :trollface::

faz.bang:trollface:

:-D

@qrush
Copy link
Author

qrush commented Apr 26, 2012

It seems like no code was produced out of that discussion, hopefully this might be a little more valuable given it works, and all of the tests are passing.

I think the "this character means something different in Ruby" argument is a bad one. The existential operator has wildly different semantics in CoffeeScript than the convention Ruby lays down for methods ending with ?.

@h3h
Copy link

h3h commented Apr 26, 2012

👎

@ngauthier
Copy link

In all seriousness, I think this is a great patch. When I started coding coffeescript (about two weeks ago!) I thought it was super lame I still had to use () to call a function.

I also think it looks a lot cleaner when chaining:

foo.bar().baz()
foo.bar!.baz!

IMO, ! means "DO IT" which is a nice way of calling a method.

@ngauthier
Copy link

also, could it be possible to do:

foo.bar!baz!

or is that too confusing?

@qrush
Copy link
Author

qrush commented Apr 26, 2012

@ngauthier it doesn't allow chaining right now. I don't think it should.

@caleb
Copy link

caleb commented Apr 26, 2012

awesome! (Or should that be: awesome()?) +1

@abedra
Copy link

abedra commented Apr 26, 2012

The context of ! is no where near as clear as () is. ! in a lot of cases means that state will be mutated, or in the case of ActiveRecord in rails, something entirely different. Adding ! here further muddies the water. The common thing in all cases is that ! means that this method will do something that you might not expect, be careful.

-1

@spicycode
Copy link

I'm not a fan either. It further deviates CS from it's golden rule of "It's just JavaScript". ☔ Pulling in @rkneufeld

@vendethiel
Copy link
Collaborator

For me, ! means "in place value modification", not "no args"

@benjreinhart
Copy link

Huge -1. In ruby, it is used for a certain purpose, to warn that the method is either going to mutate the object or that something else that's special will take place and to be careful. You want it here just so you can avoid using () which I personally find to be incorrect.

Let's not forget the reason we need parens, because functions are properties like anything else except they also have the ability to be invoked. So @ngauthier it's not lame that we have to use parens because it's common that we actually want to refer to the function without invoking it (passing it as an argument, for example).

In my opinion the parens are necessary and desired because the lack of parens means "treat this as a property" whereas the use of parens means "invoke this property".

However, I am totally for using the trollface in place of parens.

@plukevdh
Copy link

I dont want to make the argument that it should act like ruby in all cases (since, yes, it does differ in many cases). But i dont want to change syntax for the sake of developer laziness when it adds potential to confuse, muddle clarity, or decrease consistency.

Thanks to @TrevorBurnham for posting the two-year old attempt at this, which was closed as wontfix. I agree with the arguments made in that closing comment.

@michaelficarra
Copy link
Collaborator

The coffeescript equivalent of warning that a function is being used for its side effects (signalling mutation) is do fn notation. If I was using the return value of fn, I would call it with parentheses and use it as an expression.

@itspriddle
Copy link

👍 This seems a lot cleaner to me

@vojto
Copy link

vojto commented Apr 26, 2012

-1

I think it introduces a lot of confusion. I spent 20 seconds figuring out what is this about.

And it's useless.

By the way, bad example:

faz = new Foo!
# Useless, you can do:
faz = new Foo

@mcmire
Copy link

mcmire commented Apr 26, 2012

This is interesting, for sure. Not that having to type parentheses is really that "painful", I mean saving one character isn't that big of a deal for me. But aesthetically it has a certain feeling which is kind of... well, interesting.

There are other things to consider though, obviously -- readability and consistency. If you didn't know anything about CoffeeScript could you guess what this means?

foo bar(baz!, quux!)

And how would you know that this would not be valid (or would it?):

foo bar! baz!, quux!

it doesn't allow chaining right now. I don't think it should. [@qrush]

And this is going to be a sticking point with people, I suspect.

So I'm -1 for now.... but I'd be curious to see this in a real CoffeeScript file.

@goto-bus-stop
Copy link

And how would you know that this would not be valid (or would it?):

foo bar! baz!, quux!

That would be valid, just like foo bar() baz(), quux() is valid (and yields foo(bar()(baz(), quux())))

I like this, in fact I use this a lot in coco, although it's not that much of a bonus either. +0.1?


test "bang calling a function", ->
class Foo
bar: -> 3

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The evil TDD me keeps thinking this means that bar! only returns a 3. :-)

@tswicegood
Copy link

My initial reaction was +1 to this, but after reading the comments I am a bit worried that it could cause confusion. This mimics behavior in Ruby, but not fully. The examples quoted above as foo! "bar" being equivalent to foo("bar") or foo "bar".

In the end, I think I'm now +0. I'd like to see it changed around a bit to allow function to execute automatically a la Ruby, but that can't happen without some serious issues, so... :-/

@mcmire
Copy link

mcmire commented Apr 26, 2012

@renekooi:

That would be valid, just like foo bar() baz(), quux() is valid

Well, the reason I asked is, just as these are equivalent

foo! ==> foo()

would these be equivalent

foo! bar ==> foo(bar)

or would it throw a syntax error if the grammar rule is simply that if a bang comes after a word it gets converted to parentheses, a la...?

foo! bar ==> foo() bar

@goto-bus-stop
Copy link

@mcmire, I'd say ! === (). foo! bar would be foo() bar, (which is not a syntax error, by the way)

foo! bar compiling to foo(bar) would be quite a useless usage of the ! mark :)

@benjreinhart
Copy link

@tswicegood how does this mimic the intended behavior of ruby? It may at first glance look like ruby, but it does not mimic how these are supposed to be used in ruby and also, as previously stated, in CS foo! and foo() means foo(), where as, in ruby, foo means foo() and foo! means foo!()

@clutchski
Copy link

I'm not feeling this. It adds confusion (two ways to do the same thing, which should I use?), loses consistency with other languages (ruby's change in place convention, javascript's not) all to save a single character. What's the point?

@tswicegood
Copy link

@benjreinhart Thanks for the correction -- that's what I intended. It mimics it in that it looks the same, but the actual result is different. I think that's a bad thing.

@satyr
Copy link
Collaborator

satyr commented Apr 27, 2012

Yet another Coco-fication (or MoonScript):

$ coco -bce 'foo.bar!baz!'
foo.bar().baz();

I don't think Jeremy likes this one though.

@joshuaclayton
Copy link

My intention for this was to only use the ! to execute a function that you're not passing arguments to. I didn't intend on dropping periods to chain functions either.

The ideas were along the lines of:

class User
  constructor: (firstName, lastName) ->
    @firstName = firstName
    @lastName  = lastName

  fullName: ->
    [@firstName, @lastName].join " "

  sayHi: ->
    name = arguments[0] || @fullName!
    alert "Hi! I'm #{name}."

josh = new User("Josh", "Clayton")
josh.fullName! #=> "Josh Clayton"
josh.sayHi! #=> alerts "Hi! I'm Josh Clayton."
josh.sayHi "John Doe" #=> alerts "Hi! I'm John Doe."

@showell
Copy link

showell commented Apr 28, 2012

If the proposal is simple to replace "()" with "!", then I think the drawbacks far outweigh the benefits. It wouldn't cure the fact that Ruby folks are inclined to omit parens, because they're used to Ruby's very different object model, and the "!" would have different semantics than Ruby, leading to even more confusion. Not to mention the fact that there's a ton of existing code that uses "()" for no-arg calls.

I've been back in Python land for a while, and I have to say that paren-free syntax is a highly overrated feature of both CS and Ruby. Actually, there are a couple features of Ruby that you don't miss at all when back in Python. Not that Python is perfect by any stretch. I would love it if Python adopted CS's syntax for functions.

@franciscolourenco
Copy link

-1

@jashkenas
Copy link
Owner

Great -- seems like there's a clear majority opinion on this one. Closing for the same reasons as previously.

@pocesar
Copy link

pocesar commented Apr 20, 2013

the "!" actually should modify the object that is calling it, not just by "converting" the () to a !
it modifies the calling object, like reassigning to variable itself
like this:

# like doing t = t.toFixed(2)
if t.toFixed!(2) 
  t += 0.2

# or a one liner array convertor (like a filter)
i.toFixed!(2) for i in [2,3,4,5,6] 
### 
would be

var i, _i, _len, _ref;

_ref = [2, 3, 4, 5, 6];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  i = _ref[_i];
  _ref[_i] = i.toFixed(2);
}

now all the numbers in _ref would be x.00
###
# even though it's the same as doing
arr = [2,3,4,5,6] 
arr[key] = value.toFixed(2) for key,value of arr

@davidchambers
Copy link
Contributor

@pocesar: I don't think we should add sugar for o = o.method(). If you want to perform assignment, use the assignment operator. :)

@pocesar
Copy link

pocesar commented Apr 21, 2013

@davidchambers the problem is that, if it's done inside the parenthesis, it returns an array. the ! would make it differ and return the original object, like this

funct(value.toFixed(2) for key,value of {"1":1,"2":2,"3":3,"4":4,"5":5}) # passes an array to the funct, the object is lost

it would work like this

obj = {"1":1,"2":2,"3":3,"4":4,"5":5};
obj[key] = value.toFixed(2) for key,value of obj
funct(obj)

3 lines, what could be just one, since coffeescript is all about shortands and sugar.

funct(value.toFixed!(2) for value of obj) #rewrites the obj[key] automatically and passes the obj instead of an array, all because of the !

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

Successfully merging this pull request may close these issues.

None yet