Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I'm playing around a bit with my own C like DSL grammar and would like some oppinions.

I've reserved the use of "(...)" for invocations. eg:

foo(1,2);

My grammar supports "trailing closures" , pretty much like Ruby's blocks that can be passed as the last argument of an invocation.

Currently my grammar support trailing closures like this:

foo(1,2)
{
   //parameterless closure passed as the last argument to foo
}

or

foo(1,2) [x]
{
    //closure with one argument (x) passed as the last argument to foo
    print (x);
}

The reason why I use [args] instead of (args) is that (args) is ambigious:

foo(1,2) (x)
{
}

There is no way in this case to tell if foo expects 3 arguments (int,int,closure(x)) or if foo expects 2 arguments and returns a closure with one argument(int,int) -> closure(x) So thats pretty much the reason why I use [] as for now.

I could change this to something like:

foo(1,2) : (x)
{
}

or

foo(1,2) (x) ->
{
}

So the actual question is, what do you think looks best?

[...] is somewhat wrist unfriendly.

let x = [a,b] 
{
}

Ideas?

share|improve this question
    
If anyone is interested in this sort of things, here is what I have so far : rogeralsing.com/?s=plastic –  Roger Alsing Jan 11 '11 at 12:00
    
Out of interest, why are square brackets [...] wrist intensive? On my keyboard they are lowercase letters next to return, same keys as braces, easier to type than any of the other bracket types. –  Orbling Jan 11 '11 at 12:39
    
On my swedish keyboard they are altgr+8/9, so a bit wrist intensive.. –  Roger Alsing Jan 11 '11 at 13:16
    
Almost every language uses parentheses more often than square brackets, so if you're programming on a standard US keyboard, you should have them switched around so that parentheses are unshifted. –  rwallace Aug 25 '11 at 15:01
add comment

6 Answers 6

up vote 4 down vote accepted

I like Groovy's syntax, which is

foo(1,2) { x ->
   ...
}
share|improve this answer
    
I've decided to use this approach , but with ":" as separator as for now... eg foo(1,2) {arg : arg.bar == 2} –  Roger Alsing Jan 11 '11 at 12:00
    
-> or => (C# style) is neat. better than lambda key word from ruby –  lukas Jan 11 '11 at 12:56
add comment

I have always thought they should be threated as any other regular parameter.

Consider javascript

 sort( sortFunctionGoesHere );

Invoked as:

sort( function( a, b ) {
    return b - a; 
});

So, I would rather have:

foo( int, int , closure(int) )

And use it like this:

foo( 1, 2, (x) {
...    
})
share|improve this answer
    
The thing is, if you do this extensively, your code contains a lot of closing braces, that are hard to associate with their counterpart. Also, by convention, a trailing block quite clearly communicates iteration, whereas passing a function object could also be an asynchronous callback or an iteration block (and possibly other things). –  back2dos Jan 11 '11 at 11:29
    
Really? I didn't know that. I have seen this in Smalltalk and Javascript before, and in both, they were part of the parameters. It was until Ruby and the Groovy that I saw the form you mention. Anyway, interesting +1 –  OscarRyz Jan 11 '11 at 12:12
add comment

First of all, the closure parameter should be a normal part of the parameter list.

Thus, there is no fundamental difference in declaration or usage. I strongly dislike Ruby introducing special cases with yield, non-first-class functions and an &-parameter.

Just

function foreach(collection, f) 
  // code
end

so

foreach([1, 2, 3], print)

should work in any case.

That said, you can add syntactic sugar for providing a final lambda argument in a more convenient way. I see two major options, depending on your language design.

  1. Plain syntax sugar. I like @ammoQ's approach with

    foreach([1, 2, 3]) { x ->
       print(x)
    }
    

    So f(args ...) { vars -> body } is literally translated to f(args ..., λvars -> body)

  2. That's the way i.e. Scala does it. As you mentioned

    There is no way in this case to tell if foo expects 3 arguments (int,int,closure(x)) or if foo expects 2 arguments and returns a closure with one argument(int,int) -> closure(x)

    Just use this idea - a function returning another function to be called is called currying, which is a very common pattern in functional programming.

    If you have curried functions in your language as Scala does, you just need a syntax for closures with { } being equal to regular parentheses. The rest is a regular call semantic. I.e.

    function foo(a, b)(c)(f) 
    end
    ...
    foo(1, 2)(3) { x -> x + 1 }
    
share|improve this answer
    
+1 for mentioning currying. –  Peter Taylor Feb 16 '11 at 7:12
add comment

I like the C# syntax, nice and short
(x, y) => { /* code */ }

And to declare
Action< int, int>

share|improve this answer
add comment

I don't think bounding to C like syntax is good idea. I hope you checked Haskell syntax. If you're making a DSL, I think it's main purpose is abstraction of the domain. Basically, C like syntax is too verbose...

Haskell example:

-- Defining a function
add x y = x + y

-- Defining a lambda function ('\' means lambda character 'λ')
add = \ x y -> x + y
share|improve this answer
add comment

Basically, defining the argument within the block is easier, because it resolves a lot of ambiguities, for example the way Groovy does it, as @ammoQ pointed out.

Ruby also does this, but is a little easier to parse than Groovy: foo(1,2) { |x,y| ... }

This can be simply parsed from left to right and it is simpler to read. Also, chances are good, you'll never allow | as a unary operator (i.e. at the beginning of a statement), so the risk of ambiguity is extremely low.

If that is too "wrist intense", I suppose foo(1,2) { <x,y> ... } might help on english keyboards.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.