Closed alvivi closed 10 years ago
This is something I like from haskell as well. I thought the choice of using the backtick was a little funky, though. It's an awfully hard character to reach for such a useful syntax. That particular character is already taken in coffeescript anyway. Also I didn't understand why the function had to be wrapped in two backticks, seeing as the function name would be recognizable as such without having to be wrapped in anything. If we did this in coffeescript, I think it would be nice to smooth over those implementation details. Maybe something silly like prefixing the function with the %
sign?
gt = (x, y) -> x > y
5 %gt 6
%
would be ambiguous in most cases, one of those being your own example:
$ coffee -bep '5 %gt 6'
5 % gt(6);
I was thinking about this today, and I figured it might be possible to distinguish between prefix and infix by only allowing infix functions to be used in an infixy way, but then the problem is how do you distinguish between the two? I like benekastah's idea of prefixing it with something, but the question is what? We're kind of restricted by the ASCII charset.
I +1 this request, as long as one can find a nice syntax that isn't already taken by the language, or that doesn't make the parser even more ambiguous.
PS.:
As I was reading that page, the -: <expression> :-
combo seems pretty okay to me, given you can't have hyphens as part of identifiers, and the subtraction operator can't act as a post-fix (comming before he colon in an object literal).
The grammar could be:
<expression> ::= <value> | <invocation> | <code> | <operation> | ( ... )
<operation> ::= <unary> | <prefix> | <postfix> | <existential> | <infix> | ( ... )
<infix> ::= "-:" <expression> ":-"
Of course, this is not as lightweight as one could wish for. If infix expressions (those that evaluate to a function that can be applied to the lhs and rhs), we could just have -: <identifier
as a way of making a function object an infix.
I'm not too keen on the infix notation for method invocation (the a -:.method:- b
example) since method invocation are dispatched on a single objects. Applying a function object coming that's stored in an object as an infix would be okay — the infix expression could be used for that.
All in all, I think this would really make CoffeeScript a lot more expressive :3
+∞. Pinging @jashkenas - any chance to get your feedback on that?
Also, just a thought: what about allowing (pre|suff)ix-less special character operators? Its problematic because JavaScript only allows alpha-numerics and $/_ in identifiers, but maybe it can be done using some "magic object" to hold the operators as properties - e.g. OPERATORS = '**': Math.pow; 123 ** 456
-> OPERATORS['**'](123, 456)
? I don't think using a magic object is a great idea (quite awful, really)... but I would really love to have that feature.
I dunno. Why would you want to limit yourself to only having one of the arguments on the LHS? Why would this be undesirable?
a, b `infix` c, d
Probably, in most cases where you have actual objects, you won't need this in any case:
a.dividedBy b
All of the examples in this thread are for operators ... that are already real operators. How about some examples of how this would be helpful for real code? In the meantime, closing as a wontfix
.
@jashkenas Well, it's not about operators per-se but the readability (natural flow, if you will). You're right that objects solve part of this problem, but it's not really practical to have all operations on objects, all the more given the high use of functional idioms in JavaScript (and I believe more so in CoffeeScript, from people not coming from strict OO-only languages).
So, with objects you have a single dispatching where the single item on the LHS is the most important thing in the equation, and this is still restrictive. For example:
var slice = () -> [].slice.call.apply [].slice, arguments
var sequence = { 0: 1, 1: 2, length: 2 }
With usual message passing for OO idioms, this looks okay
sequence.__proto__ = Array.prototype
sequence.slice 1 # => [2]
With the usual function application idioms, the application looks awkward
slice sequence, 1 # => [2]
With the ability to define any function as infix, you can get the same natural reading from idiomatic OO dispatching, but without the need to monkey-patch the object (so, less overhead and a lot safer)
sequence `slice` 1 # => [2]
This is all good for the usual single-dispatching from OO-ish idioms. But indeed functional idioms allow people to do multiple dispatching, as it's not apparent who are the main players in a function application. Magpie solves this nicely by allowing you to define arbitrary number of arguments to come before the function's identifier, and arbitrary to come after the function's identifier.
def (this is Person, action is Verb) to (predicate is String)
print("Let it be know that " + this name + action + " to " + predicate)
end
Alan went to "the church"
# => "Let it be know that Alan went to the church"
( You can read more about Magpie's handling of function application here: http://magpie.stuffwithstuff.com/calls.html )
...just some possible infix application.
# string comparison
like = (strOne, strTwo) -> strOne.toLowerCase() is strTwo.toLowerCase()
a = 'some string'
b = 'SOME string'
if a `like` b then do_something()
# something more useful, class implement
implements = (base, props) -> base::[key] = value for value, key in props
class UsefulStuff
method1: (does) -> things()
method2: (is) -> @extremely_useful
someProps =
method3: (also) -> a_neat method
importantNumber: 123
UsefulStuff `implements` someProps
(new UsefulStuff).importantNumber is 123 # true
...becomes...
// comparison
if(like(a, b)) do_something();
// class
function UsefulStuff(){ /*etc*/ }
implements(UsefulStuff, someProps);
In my humble opinion, base 'implements' properties
(apparently backticks are not to be escaped) looks nicer/cleaner than implements base, properties
...
@jashkenas I'm not familiar with the Haskell style, but it seems similar to the F# pipelining operator. Its really useful for stuff like filtering, sorting, etc. on a collection/array. Here's one example of how this is used in F#:
Dealer.cards()
|> Seq.map (fun c -> (rand.Next(), c))
|> Seq.sortBy fst
|> Seq.map snd
|> Seq.take 10
|> Seq.toList
This converts the sequence of cards into a tuple containing the card and a random number, sorts by the random number, gets the second element of each tuple (converting it back into a sequence of cards), gets the first 10 cards, and converts it to a list.
It's equivalent to the following F# code:
Seq.toList
(Seq.take 10
(Seq.map snd
(Seq.sortBy fst
(Seq.map (fun c -> (rand.Next(), c))
Dealer.cards()))))
As you can see, the first way is much more readable.
(In case you get confused when reading this code, F# does not need to seperate the arguments for functions.)
How about some examples of how this would be helpful for real code
Why I personally find this feature very exciting:
obj ++ { a: 1, b: 2 }
(would work like _.extend()
)parent <| { a: 1, b: 2 }
(as suggested for ES.next)str =~ /foo/
(-> str.match /foo/
)[1,2,3] |> (n)->n+1
(-> [1,2,3].map ...
. Yeah, the |>
operator is kinda confusing as it looks similar to <|
but has nothing to do with. I could probably find a better one for that)IMHO having that feature, in combination with CoffeeScript's syntax, would make CoffeeScript one of the best languages (that I know of) for DSLs, fluent-style/literal programming and customization by the developer.
Very sad that this issue is 2 years old and still not implemented. One more really regular usage is extend
objects
options = ^defaults extendWith options or {}
It's so beautiful!
And here my proposition on syntax - it's ^
before first parameter.
It's like a note which indicates that here goes infix expression.
And also ^
not used in coffee.
Here above example with inline object instead of defaults
options = ^(
data: "id=1"
url: "http://"
) extendWith options or {}
I think full syntax for infix should be ^(...) functionOrMethodName arg2, arg3, ...
.
Brackets can be avoided if in it not some expression.
So here valid examples
^variable functionName param1, param2
^functionName(params) functionName param1, param2
^object.field functionName param1, param2
^object.method(params) functionName param1, param2
^(variable + 1) functionName param1, param2
^(
field1: 1
field2: 2
) functionName param1, param2
Sorry, I forgot about xor operator ^
, maybe instead of it we can use :
options = :defaults extendWith options or {}
@michaelficarra Care to share why reopened - still applicable?
The examples here seem to come from land of custom DSLs, and I think that CoffeeScript can be a target for these but probably shouldn't force them onto the general public. This goes along requests for macros, operator overloading etc. - with great power comes great mess (C).
@xixixao: I'm not sure why we re-opened it. I am personally in favour of the proposal. But you're right, this just wouldn't be consistent with other, similar design decisions we've made.
Then let's put it down for now.
It will be nice to have infix function/method application in coffeescript, just like Haskell does. This would improve the look of EDSLs in coffeescript. Here is an example of infix function application:
Infix method application could be implemented like this:
Haskell also allows infix expressions like
a 'foo x + 1, y' b # foo(x + 1,y,a,b);
and function operators are not restricted by its aritya 'foo' b, x, y # foo(a,b,x,y)
.