Nice TWiki > Dev > FeatureProposals > BlockCallSyntax (r1.11) TWiki webs:
Dev | Doc | Main | TWiki | Sandbox
Dev . { Changes | Index | Search | Go }
It would be good to allow a different syntax for method application in some circumstances. Specifically, it would be nice if:
foo
{
 baz();
}

could expand into

foo(()=> 
  { 
     baz();
  });

Basically, if a function call is followed immediately by a block, then the block is converted to a void->A function, where A is the return type of the function, and treated as the last argument to the function call. This part is easy to implement. But what if the block isn't the last argument or there are multiple blocks?

This would allow us to add features like C#'s 'using' keyword, without having to modify the syntax every time we want a new one: Do you have other examples than 'using'?


<A> A using(Disposable resource, void->A func)
{
  try
  {
     return func();
  }
  finally
  {
    resource.dispose();
  }
}

let stream = new FileInputStream("foo");
let value = using(stream) { char c = stream.read(); }

Of course, we might also want something other than a void->A function, perhaps we'd like to have a version of 'using' that doesn't require to first bind the used object to a name. We could solve this by instead expanding the block function into a function which takes exactly the same arguments, with the same names, that the named function takes:

//Assume an abstract interface for disposables here, makes the
//typing nicer.
<Disposable A, B> B using(A resource, A->B func)
{
  try
  {
     return func(resource: resource);
  }
  finally
  {
    resource.dispose();
  }
}

I think it's more consistent to allow argument of function types to be named.

<Disposable A, B> B using(A resource, (resource A)->B func)
{
  try
  {
     return func(resource);
  }
  finally
  {
    resource.dispose();
  }
}

so now we can do

using(new FileInputStream("foo"))
{
  //use the file input stream with the name 'resource':
  char c = resource.read();
}

which the compiler will translate to:

using(new FileInputStream("foo"), (FileInputStream resouce) =>
  {
    //use the file input stream with the name 'resource':
    char c = resource.read();
  });

-- BrynKeller - 31 Oct 2003

The problem with this is that you can't choose the name of the variable so you end up with an general name. And it would get problematic when you nest 'using statements'. I'd rather write something like the following although I have no idea how that would look like on the implementation side of the function.

using(myFile = new FileInputStream("foo"))
{
 //do something with it
}

added some comments in italic -- ArjanB - 08 Nov 2003


I don't know why anyone hasn't recognized it yet, but you are getting closer and closer to the syntax of Ruby. I like the stronly typed-ness of Nice, but Ruby has tons of cool syntax sugar that I would also like to see in Nice. Blocks (or Closures) are only one of them. Many classes in Ruby have been designed to make heavy use of blocks passed to methods. I don't like some of the Ruby syntax, such as the yield call. But still, it would be good for the Nice compiler developers to take a close look at Ruby, and to adopt what they like. It's interesting that Nice has already adopted quite a bit of what Ruby has. They are both interesting languages. I can see, though, that Nice can more easily handle enterprise level projects than Ruby. Here're some links to learn about Ruby:
http://www.ruby-lang.org/en/
http://www.sofer.com/ruby/
http://www.rubycentral.com/

-- TroyHeninger - 12 Nov 2003

Seems more obvious to look at groovy. Maybe Nice has more in common with FlowJava Jiazzi and MultiJava than with scripting languages.

-- IsaacGouy - 31 Dec 2003


Maybe I should look at C# using before commenting but...

How am I supposed to figure out that this

  loop(5) { doSomeThing(); }

is shorthand for this

  loop(5, () => { doSomeThing(); } );

That doesn't look like the same kind of thing; this does

  loop(5, { doSomeThing(); } );

Smalltalk avoids the ugly syntax because blocks (Smalltalk anonymous functions) are objects with defined methods, so we can write something equivalent to { doSomeThing(); }.loop(5) and still have consistent language.

  loop(5) { doSomeThing(); } doesn't seem to fit with the existing forms in the language - it doesn't look like function application; it looks like a keyword but it isn't a keyword.

-- IsaacGouy - 08 Jan 2004

I don't think you should look at it as a shorthand for something else. It's more like a user defined statement.

BTW we are considering making () => doSomeThing(); } equivalent to { doSomeThing(); )

-- ArjanB - 08 Jan 2004

Does user defined statement solve some problem? (Whatever happened to my spirit of adventure!)

Here are some funky apply-to-all expressions

static void Main() {
qs = Queens.queens(7);
qs.{ Queens.printboard(it); };
Console.ReadLine();
}

-- IsaacGouy - 09 Jan 2004

The implicit 'it' variable has problems when you try to nest apply-to-all. apply-to-all looks superfluous for Nice because of the alternatives.

for(it : qs) Queens.printboard(it);
qs.foreach(it => Queens.printboard(it));

-- ArjanB - 09 Jan 2004

Fine with me - I find the qs.{ Queens.printboard(it); }; too cryptic.

-- IsaacGouy - 14 Jan 2004

Notice in the syntax that is currently implemented you have this scoping problem:

    let f = new FileOutputStream("foo");
    using(f)
    { write(f, getBytes("Hello World!"));
    }
    write(f, getBytes("Hey, Please don't let me write to a closed stream"));

The scope of myFile should be inside the using block, not outside the block, so that the second call to write above should fail. This is the same reasoning that allows:

    for (int i = 0; i < x; ++i) { }
as a shortcut for:
    int i;
    for (i = 0; i < x; ++i) { }

For scoping, the example with let above is not particular to the way BlockCallSyntax is implemented, it's the normal rule. We are working on generalizing the for (int i = ...) syntax to arbitrary expressions. This will make the language more coherent, and in particular allow to use it with calls like using, which will allow you to have the scoping that you rightfully want. -- DanielBonniot - 01 Feb 2004

What is the advantage of using(f) { ... } over using(f, { ... });? I agree it is prettier but it looks like a language construct like synchronized instead of a library function. The second form seems more intuitive and is extensible to multiple block parameters:

    <T, U, V | U <: T, V <: T>
    T if2(boolean b, void -> U thenExpr, void -> V elseExpr)
   = b ? thenExpr() : elseExpr();
    
    if2(x < y, { thenDoSomething(); }, { elseDoSomething(); });
BTW, if2 is how the "built in" conditional statements are done in CecilLanguage?: http://www.cs.washington.edu/research/projects/cecil/www/Release/doc-cecil-stdlib/node13.html

-- BrianSmith - 01 Feb 2004

The good thing about BlockCallSyntax is that, although its use will at first seem look like language constructs, it is actually a user-definable form. So after you learn that fact, you see the language as more uniform and coherent. And it will be interesting to see what uses users find for it. For instance, Bryn used it quite cleverly I think in his article about his logging library.

It's true that it does not apply to multiple block parameters, but that could be handled later by extending the syntax. -- DanielBonniot - 01 Feb 2004

Topic BlockCallSyntax . { Edit | Attach | Ref-By | Printable | Diffs | r1.16 | > | r1.15 | > | r1.14 | More }
Revision r1.11 - 01 Feb 2004 - 20:39 GMT - DanielBonniot
Parents: WebHome > FeatureProposals
Copyright © 1999-2003 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback.