New Dart feature proposal: method cascades

229 views
Skip to first unread message

Bob Nystrom

unread,
Nov 21, 2011, 8:48:20 PM11/21/11
to General Dart Discussion
Jacob Richman, one of the people feverishly working on our new DOM library has a language proposal to share with you. You can see it in Google doc form here: https://docs.google.com/document/d/1U0PeHtVQHMQ8usy7xI5Luo01W5LuWR1acN5odgu_Mtw/edit.

One of the questions that's come up a few times with our DOM API is whether or not it supports jQuery-style chaining. This proposal is answer to that.

jQuery is one of the most well-known examples of APIs designed around a fluent interface. StringBuffer is another. EaselJS (http://easeljs.com/) tries to make HTML Canvases easier to use by wrapping them in this style.

But note that all of those APIs had to be designed to specifically work with that pattern. Also note that they work with methods, but not getters and setters.

Jacob, Erik Arvidsson, and Alex Russell spent some time going over this and they came up with a solution that lets you use that style with any existing API, without having to contort your library around. It also works with getters and setters.

For some context, here's what a bunch of DOM manipulation looks like in the current API:

  final myTable = document.query('#myTable');
  final ElementList firstColumn =
    myTable.queryAll('.firstColumn');
  firstColumn.style.background = 'red';
  firstColumn.style.border =
    '2px solid black';
  firstColumn.text = 'first column';

  final ElementList lastColumn =
    myTable.queryAll('.lastColumn');
  lastColumn.style.background = blue;
  lastColumn.text = 'last column';

Kinda tedious. jQuery would give you:

  $('#myTable')  
      .find('.firstColumn')  
          .css({'background': 'red',
                ‘border’: ‘2px solid black’})
          .text(‘first column’)  
      .end()  
      .find('.lastColumn')  
          .css('background','blue')
          .text(‘last column’);  

With this proposal, it's:

  document.query('#myTable').{
    queryAll('.firstColumn').{
      style.{
        background = 'red',
        border = '2px solid black'
      },
      text = 'first column'
    },
    queryAll('.lastColumn').{
      style.background = 'blue',
      text = 'last column'
    }
  };

Pretty hot, right? Read the proposal for the details.

FAQ: 

Q: Oh, horror, did you just recreate JavaScript's awful "with"?
A: NO. Nooo. There's no dynamic scoping or anything crazy going on here. It desugars trivially to simple code with sane name lookup. It's all groovy.

Q: Isn't this just message cascades in Smalltalk?
A: Yup. Imitation is the sincerest form of flattery.

- bob

Michal Mocny

unread,
Nov 21, 2011, 11:52:55 PM11/21/11
to General Dart Discussion
+1

It is very clear how setters would work, but I am not clear on
getters. What is the return type of any "compound/cascaded" method
invocation for that matter? A tuple of each of the return values?
void? (I can't seem to open the google doc you link to to possibly
find the answer myself, sorry).

Still, the syntax seems and semantics of your proposal appear
extremely desirable.

On Nov 21, 8:48 pm, Bob Nystrom <rnyst...@google.com> wrote:
> Jacob Richman, one of the people feverishly working on our new DOM library
> has a language proposal to share with you. You can see it in Google doc

> form here:https://docs.google.com/document/d/1U0PeHtVQHMQ8usy7xI5Luo01W5LuWR1ac...

> *FAQ: *

Jan

unread,
Nov 22, 2011, 2:27:22 AM11/22/11
to mi...@dartlang.org
Awesome, I love it.

Dirk Detering

unread,
Nov 22, 2011, 3:03:46 AM11/22/11
to Michal Mocny, General Dart Discussion

Haven't seen the doc too, but from the presented code it seems that 'style' is already a getter. That means, you use getters by further calling methods on their result.
In the end the whole cascading seems not to be meant to return some value but to declare a call chain (manipulation chain). After finishing, the document is modified.

Ladislav Thon

unread,
Nov 22, 2011, 5:34:06 AM11/22/11
to General Dart Discussion
  document.query('#myTable').{
    queryAll('.firstColumn').{
      style.{
        background = 'red',
        border = '2px solid black'
      },
      text = 'first column'
    },
    queryAll('.lastColumn').{
      style.background = 'blue',
      text = 'last column'
    }
  };

Love this on the first sight! Two questions, maybe...

Why use comma instead of semicolon as a separator? Is it because the separated things are expressions and not statements? Semicolon would be more natural I guess, it's already used as statement separator in Dart... well, not as a separator, it's an end-mark. Comma as a separator is a nice distinction, but maybe using end-marks and semicolons would be more consistent with rest of the language.

How about allowing return rExpr as a last expression, meaning that the value of the expression wouldn't be the receiver, but rExpr? It might allow some nice simplifications, but I need to think about it some more.

Oh, and one nice observation. Let's imagine

class C {
  m1() {...}
  m2() {...}
  noSuchMethod() {...}
}

I think that this

c = new C();
c.{
  m1(),
  m2(),
  m3(),
  m4()
}

would result in calls c.m1(), c.m2(), c.noSuchMethod(...'m3'...) and finally c.noSuchMethod(...'m4'...), which allows for some nice Groovy-style builders, yay!

LT

Bob Nystrom

unread,
Nov 22, 2011, 2:03:33 PM11/22/11
to Michal Mocny, General Dart Discussion
On Mon, Nov 21, 2011 at 8:52 PM, Michal Mocny <mmo...@google.com> wrote:
+1

It is very clear how setters would work, but I am not clear on
getters.  What is the return type of any "compound/cascaded" method
invocation for that matter?

It returns the original receiver:

var element = document.query('.blah');
var result = element.{
  text = 'something,
  style.{
    background = 'red'
  }
}

assert(element === result);

 
 A tuple of each of the return values?
void? (I can't seem to open the google doc you link to to possibly
find the answer myself, sorry).

Hmm. I'll bug Jacob to look into that.
 

Still, the syntax seems and semantics of your proposal appear
extremely desirable.

Thanks!
- bob

Bob Nystrom

unread,
Nov 22, 2011, 2:06:27 PM11/22/11
to Ladislav Thon, General Dart Discussion
On Tue, Nov 22, 2011 at 2:34 AM, Ladislav Thon <lad...@gmail.com> wrote:
Why use comma instead of semicolon as a separator?

Hmm, good question. I remember we went back and forth on this a bit. I thought we'd ended up on semicolons, but I'm not sure.
 
Is it because the separated things are expressions and not statements? Semicolon would be more natural I guess, it's already used as statement separator in Dart... well, not as a separator, it's an end-mark. Comma as a separator is a nice distinction, but maybe using end-marks and semicolons would be more consistent with rest of the language.

How about allowing return rExpr as a last expression, meaning that the value of the expression wouldn't be the receiver, but rExpr? It might allow some nice simplifications, but I need to think about it some more.

We talked a bit about what the result of the cascade expression is. Right now it's the receiver and I think that's probably the best answer but if that proves cumbersome we could explore other options. I think in most cases it won't matter because you'll likely be using this in a statement context where the result is discarded anyway.
 

Oh, and one nice observation. Let's imagine

class C {
  m1() {...}
  m2() {...}
  noSuchMethod() {...}
}

I think that this

c = new C();
c.{
  m1(),
  m2(),
  m3(),
  m4()
}

would result in calls c.m1(), c.m2(), c.noSuchMethod(...'m3'...) and finally c.noSuchMethod(...'m4'...), which allows for some nice Groovy-style builders, yay!

:D

Using this DSLs was definitely a design consideration.

- bob

Jacob Richman

unread,
Nov 22, 2011, 2:25:07 PM11/22/11
to Ladislav Thon, General Dart Discussion

On Tue, Nov 22, 2011 at 2:34 AM, Ladislav Thon <lad...@gmail.com> wrote:
Why use comma instead of semicolon as a separator? Is it because the separated things are expressions and not statements? Semicolon would be more natural I guess, it's already used as statement separator in Dart... well, not as a separator, it's an end-mark. Comma as a separator is a nice distinction, but maybe using end-marks and semicolons would be more consistent with rest of the language.

We chose comma instead of semicolon as the separator in an attempt to make it clearer that these are special expressions not arbitrary statements.  However, I agree that ";" has advantages as well so this was a painful choice.  If we could figure out how to make it clearer that you are in the special cascade context then I'd be fine with semicolon.  If we used semicolon it would still be important to make the last semicolon optional so that you could write:

element.style.{background = 'red'; border = '2px solid black'}

which I believe reads significantly better than:

element.style.{background = 'red'; border = '2px solid black';}
 
How about allowing return rExpr as a last expression, meaning that the value of the expression wouldn't be the receiver, but rExpr? It might allow some nice simplifications, but I need to think about it some more.

I don't believe return is needed.  If you need to change the context, invoke the method outside of the cascade.  For example:

return c.{
  m1(),
  m2(),
  m3(),
  return m4()
};

Could just as easily be written as:
return c.{
  m1(),
  m2(),
  m3()
}.m4();

Is there a more complex case where the return would be useful?

 
Oh, and one nice observation. Let's imagine

class C {
  m1() {...}
  m2() {...}
  noSuchMethod() {...}
}

I think that this

c = new C();
c.{
  m1(),
  m2(),
  m3(),
  m4()
}

would result in calls c.m1(), c.m2(), c.noSuchMethod(...'m3'...) and finally c.noSuchMethod(...'m4'...), which allows for some nice Groovy-style builders, yay!

I like you're example. You are correct that noSuchMethod(...'m4'...) would be called allowing for some nice Groovy-style builders.
 
LT

Michal Mocny

unread,
Nov 22, 2011, 3:41:28 PM11/22/11
to Bob Nystrom, General Dart Discussion
I managed to open the doc a bit later (and left some comments which you promptly addressed).

The return value being the original receiver makes a lot of sense.  There are other ways to create tuples out of multiple return values etc.

Renat Zubairov

unread,
Nov 23, 2011, 2:47:22 AM11/23/11
to General Dart Discussion
Like the proposal, though +1 to semicolons instead of commas.

skyronic

unread,
Nov 23, 2011, 5:12:33 AM11/23/11
to General Dart Discussion
Hi,

I hope I can suggest a small change. What if there's code that has
side effects that needs to come between the chain.

Consider this code:

var firstCol = $("#myTable")
.find('.firstColumn')
.text('First Column');

var color = this.getColorForFirstColumn();
// Let's assume this function has some side effects, so it's very
important to call it right after the text is set.

firstCol.css('background', color);

Even the syntactical sugar of method cascading will require us to
break for code coming between the chain. What if there's some way to
let a piece of code execute as-is while desugaring. like this

document.query("#myTable").{
queryAll('.firstColumn').{

text = "First Column",
{ var newBgColor = getColorForFirstColumn(); },
style.background.color = newBgColor
}
}

Thanks,
Anirudh

On Nov 22, 6:48 am, Bob Nystrom <rnyst...@google.com> wrote:
> Jacob Richman, one of the people feverishly working on our new DOM library
> has a language proposal to share with you. You can see it in Google doc

> form here:https://docs.google.com/document/d/1U0PeHtVQHMQ8usy7xI5Luo01W5LuWR1ac...

> *FAQ: *

Dirk Detering

unread,
Nov 23, 2011, 6:31:39 AM11/23/11
to skyronic, General Dart Discussion


Am 23.11.2011 11:12 schrieb "skyronic":
> document.query("#myTable").{
>   queryAll('.firstColumn').{
>       text = "First Column",
>       { var newBgColor = getColorForFirstColumn(); },
>       style.background.color = newBgColor
>   }
> }
>

First, this specific example could simply avoid the variable newBgColor and call the getter directly instead.

Second, yes, you are right. In general this shows why cascading is only an insufficient analogue to Groovy's builder syntax, which is based on the concept of closures and delegate, not cascades.

To be honest, I don't like your syntax proposal, as the braces around the var decl suggest another scope, so the var wouldn't be visible in the following line.

KR
Det

Ladislav Thon

unread,
Nov 23, 2011, 7:04:09 AM11/23/11
to Dirk Detering, skyronic, General Dart Discussion

Second, yes, you are right. In general this shows why cascading is only an insufficient analogue to Groovy's builder syntax, which is based on the concept of closures and delegate, not cascades.


True. Cascade is a special case of a closure with delegate set to the receiver object and resolving set to "delegate only" (or delegate first?). It has the advantage of being syntax-only, not having runtime consequences. And it also has the disadvantage of being syntax-only, not having runtime consequences.

LT

Anirudh Sanjeev

unread,
Nov 23, 2011, 7:30:17 AM11/23/11
to Dirk Detering, General Dart Discussion
Hi,

On Wed, Nov 23, 2011 at 5:01 PM, Dirk Detering <mail...@googlemail.com> wrote:
>
> Am 23.11.2011 11:12 schrieb "skyronic":
>> document.query("#myTable").{
>>   queryAll('.firstColumn').{
>>       text = "First Column",
>>       { var newBgColor = getColorForFirstColumn(); },
>>       style.background.color = newBgColor
>>   }
>> }
>>
>
> First, this specific example could simply avoid the variable newBgColor and
> call the getter directly instead.

Yes, it's a poor example. A better example would have been a function
to Hide a loading div

document.query("#myTable").{
queryAll(.firstColumn).{


text = "First Column",

{ document.query("#loading").hide() },
style.background.color = "red"
}
}

> To be honest, I don't like your syntax proposal, as the braces around the
> var decl suggest another scope, so the var wouldn't be visible in the
> following line.

Yes. I knew there would obviously some issues with scoping when I
suggested it. I'm sure there are also some other subtle side-effects
that will come in.

That said, I feel that method cascading would be a fantastic feature
in the current proposed form, and maybe my proposal won't fit into the
simplicity and elegance of the existing proposal.

Thanks,
Anirudh

John

unread,
Nov 23, 2011, 9:19:50 AM11/23/11
to General Dart Discussion
+1

I would advocate support for what .net has implemented along these
lines: extension methods.

If dart supported extension methods, then chaining would be naturally
supported.

John

On Nov 21, 7:48 pm, Bob Nystrom <rnyst...@google.com> wrote:
> Jacob Richman, one of the people feverishly working on our new DOM library
> has a language proposal to share with you. You can see it in Google doc

> form here:https://docs.google.com/document/d/1U0PeHtVQHMQ8usy7xI5Luo01W5LuWR1ac...

> *FAQ: *

Dirk Detering

unread,
Nov 23, 2011, 10:42:19 AM11/23/11
to John, General Dart Discussion

Sorry, can't follow you here.
Could you please elaborate a bit more how EMs would solve this use case?

Michal Mocny

unread,
Nov 23, 2011, 11:16:03 AM11/23/11
to Bob Nystrom, General Dart Discussion
So I had a little brainstorm here this morning and realized you can easily emulate this feature using current Dart features.  Here it is: http://try.dartlang.org/s/F9Eh

This emulation could be made more syntactically pleasant with:
(a) extension method for type Object, negating the need to use the wrapper (I'm going to research "mirroring" proposal I've heard about to see if that could work.  Suggestions welcome.)
(b) (perhaps) clever use of operator overloading to replace wordy call() function

The downsides are:
- less pleasing syntax, might not even be much of an improvement over current approach of just assigning the intermediate objects to variables
- performance overhead
- mandatory naming of receiver (arguably a good thing)
- need to call .release() (or .build()) to get the actual object back (Note: only true if there isn't a way to add extension methods to Object)

The upsides are:
- currently legal dart syntax
- accomplishes the original goal without needing library changes (Note: though another way to solve lack of extension methods would be for libraries to mixin the functionality -- not ideal)
- 100% normal dart syntax within the block since its just an ordinary lambda (can issue print statements without hacks, etc).
- unambiguous syntax (lhs names aren't magically bound to receiver -- orignal Point() example line "y = y + p.y + p.x" is quite confusing)

-Michal

Dirk Detering

unread,
Nov 23, 2011, 12:01:58 PM11/23/11
to mi...@dartlang.org


Sorry Michal, again I cannot follow.

The main point in your code is:

var p = casc(new Point()).call((p){
    p.log('start');
    p.x = x;
    p.scale(10);
    p.log('scaled');
    p.x++;
    p.y = x + p.x + p.y; // Note: p need not bind to outer scope on rhs here!
  }).release();

But where is this any different from:

var p = new Point();
p.log('start');
p.x = x;
p.scale(10);
p.log('scaled');
p.x++;
p.y = x + p.x + p.y; // Note: p need not bind to outer scope on rhs here!

 ??

The point is, that you are not cascading method calls, like the original proposal suggests, as you always have to put the receiver in front anyway.

The cascading indeed is about to get free from the repeated  ‘p.’  

For your example:

var p = new Point();
p. { log('start'),
        x = x,            // ??
       scale(10),
       log('scaled'),
       x++,
       y = x + p.x + p.y
     }

KR
Det
 

Feed: General Dart Discussion Google Group
Posted on: Wednesday, November 23, 2011 5:16 PM
Author: mmo...@google.com (Michal Mocny)
Subject: Re: [misc] Re: New Dart feature proposal: method cascades

 

So I had a little brainstorm here this morning and realized you can easily
emulate this feature using current Dart features. Here it is:

[link]

This emulation could be made more syntactically pleasant with:
(a) extension method for type Object, negating the need to use the wrapper

Artikel anzeigen...

--
BITMARCK SOFTWARE GMBH
Firmensitz: Paul-Klinger-Strasse 15, 45127 Essen
Geschaeftsfuehrer: Reiner Kuhn
Registergericht: Amtsgericht Essen HRB 20680

***********************************************************************

Die Information in dieser E-Mail ist vertraulich und ist ausschliesslich
fuer den/die benannten Adressaten bestimmt. Ein Zugriff auf diese
E-Mail durch andere Personen als den/die benannten Adressaten ist
nicht gestattet. Sollten Sie nicht der benannte Adressat sein, loeschen
Sie bitte diese E-Mail.

Bob Nystrom

unread,
Nov 23, 2011, 2:04:49 PM11/23/11
to Ladislav Thon, Dirk Detering, skyronic, General Dart Discussion
On Wed, Nov 23, 2011 at 4:04 AM, Ladislav Thon <lad...@gmail.com> wrote:

Second, yes, you are right. In general this shows why cascading is only an insufficient analogue to Groovy's builder syntax, which is based on the concept of closures and delegate, not cascades.


True. Cascade is a special case of a closure with delegate set to the receiver object and resolving set to "delegate only" (or delegate first?).

It's more restricted than that. It's not just that the implicit receiver is being rebound. It's that top-level expressions in a cascade, and only top-level expressions have an implicit receiver that must be the LHS of the ".{". For example, given:

foo.{
  bar(bang)
}

Here, "bar" must be a method on "foo". Meanwhile, "bang" will not try to use "foo" as an implicit receiver: it's just a lexically-scoped name.

This makes it a good bit more limited than Groovy-style DSLs but it also avoids the complexity and very painful performance charactersistics of the dynamic scoping that those (and JavaScript's "with") lead to. 

- bob

Jacob Richman

unread,
Nov 23, 2011, 2:07:32 PM11/23/11
to Michal Mocny, Bob Nystrom, General Dart Discussion
I agree that if you want to perform cascading using existing legal dart syntax this is about as good a job as you can do.  However, the additional verbosity is a show stopper for me when multiple levels of cascading are desired as is common when using a fluent DOM API such as jQuery.

When considering alternate syntax I like to use the following simple jQuery example which requires multiple nested cascades.

return document.query('#myTable').{

queryAll('.firstColumn').{
  style.{background = 'red', border = '2px solid black'},
  text = 'first column'
},
queryAll('.lastColumn').{
  style.background = 'blue',
  text = 'last column'
}
};

Stanislav Baranov

unread,
Nov 23, 2011, 2:10:57 PM11/23/11
to Dirk Detering, mi...@dartlang.org
Michal, your idea can be simplified further by defining a helper like this:

with(obj, func) => new Cascader(obj).call(func).release();

var p = with(new Point(), (p) {
  p.log('start');
  p.x = x;
  p.scale(10);
  p.log('scaled');
  p.x++;
  p.y = x + p.x + p.y;
});

This is quite nice. Still, the additional brevity of the original proposal seems desirable:

var p = new Point().{
  log('start'),
  x = x,
  scale(10),
  log('scaled'),
  x++,
  y = x + p.x + p.y
};

Jacob Richman

unread,
Nov 23, 2011, 2:18:37 PM11/23/11
to Stanislav Baranov, Dirk Detering, mi...@dartlang.org
Note that unless Dart supports generic methods, you will lose out on static type checks by defining a helper method like "with".

Once multiple levels of cascading come into play this syntax starts to feel a little overly verbose even when a terse variable name like $_ is used.

return with(document.query('#myTable'), ($_) {
with(queryAll('.firstColumn'), ($_) {
  
with($_.style, ($_) {
    $_.background = 'red';
    $_.border = '2px solid black';
  });
  $_.text = 'first column';
});
with(queryAll('.lastColumn'), ($_) {
  $_.style.background = 'blue';
  $_.text = 'last column';
});
});

Mars Saxman

unread,
Nov 23, 2011, 2:27:32 PM11/23/11
to General Dart Discussion
On Nov 21, 7:48 pm, Bob Nystrom <rnyst...@google.com> wrote:
> One of the questions that's come up a few times with our DOM API is whether
> or not it supports jQuery-style chaining. This proposal is answer to that.


A small syntax addition would make this style of API much easier for immutable classes. I propose the addition of the following clause to the production 'compoundAssignmentOperator':

| '.='

That is, just as the statement 'foo += bar' is shorthand for 'foo = foo + bar', the statement 'foo.bar(baz)' would be shorthand for 'foo = foo.bar(baz)'.

With immutable classes, one implements "mutator" methods by returning a new object instance. When using such a class, one often wants to replace the reference to the original instance with the result from the method call. Imagining an immutable queue, for example, one might enqueue like so:

foo = foo.enqueue( 42 )

This naturally supports the chaining style, since the result of each method call is an object:

foo = foo.enqueue( 42 ).enqueue( 101 )

The new operator I suggest would allow you to write this chain like so:

foo.=enqueue(42)
.enqueue(101)

Immutability is a very useful concept, particularly in a concurrent world, and I would love to see Dart open up to that style of class design.

-Mars

Ladislav Thon

unread,
Nov 23, 2011, 2:42:45 PM11/23/11
to Bob Nystrom, Dirk Detering, skyronic, General Dart Discussion
True. Cascade is a special case of a closure with delegate set to the receiver object and resolving set to "delegate only" (or delegate first?).

It's more restricted than that. It's not just that the implicit receiver is being rebound. It's that top-level expressions in a cascade, and only top-level expressions have an implicit receiver that must be the LHS of the ".{".

True.
 
This makes it a good bit more limited than Groovy-style DSLs but it also avoids the complexity and very painful performance charactersistics of the dynamic scoping that those (and JavaScript's "with") lead to.

I think that dynamic scoping is a perfect solution for some problems (e.g. scoping of objects in web application -- some are global, some are session-scoped, some are request-scoped, etc. This can of course be solved otherwise, but dynamic scoping fits perfectly here. And of course Groovy-style builders, I think I'm starting to be really boring with them :-) ), but I understand the performance issues. And it might be confusing for some people. Cascade looks like a nice compromise, but we'll have to see how it works out.

LT
Message has been deleted

Andrew

unread,
Nov 24, 2011, 6:01:10 AM11/24/11
to General Dart Discussion

The jQuery below looks very pretty.

> Kinda tedious. jQuery would give you:
>
>   $('#myTable')
>       .find('.firstColumn')
>           .css({'background': 'red',
>                 ‘border’: ‘2px solid black’})
>           .text(‘first column’)
>       .end()
>       .find('.lastColumn')
>           .css('background','blue')
>           .text(‘last column’);
>

This, on the other hand, looks like a confusing mess of out-of-place
and nested braces:

Dirk Detering

unread,
Nov 24, 2011, 9:03:42 AM11/24/11
to Andrew, General Dart Discussion


Am 24.11.2011 12:01 schrieb "Andrew" <thef...@ozemail.com.au>:
>
>
> The jQuery below looks very pretty.
>
> > Kinda tedious. jQuery would give you:
> >
> >   $('#myTable')
> >       .find('.firstColumn')
> >           .css({'background': 'red',
> >                 ‘border’: ‘2px solid black’})
> >           .text(‘first column’)
> >       .end()
> >       .find('.lastColumn')
> >           .css('background','blue')
> >           .text(‘last column’);
> >
>
> This, on the other hand, looks like a confusing mess of out-of-place
> and nested braces:

Hmmm. Interesting... It is right the opposite to me.
While the jquery looks like the nasty fluent interface in Java, where it is kind of a wannabe DSL, the proposal seems
very symmetric and clearly structured.
Parens are only for query params and not for query and setters alike.
Curlies are only for cascading and not for param grouping,
so no place where parens and braces are directly nested.
Properties are always set with equal expressions, not a mix of
property , arg  and  property : arg

All in all the optical pattern is:
    XXXXX. {
        yyyyyyy
        yyyyyyy
    }

Which is almost identical to If or Loop constructs.

Message has been deleted

Klāvs Priedītis

unread,
Nov 24, 2011, 11:54:36 AM11/24/11
to General Dart Discussion
I recently had to work with VisualBasic a bit and I noticed an
interesting statement called WITH. (http://msdn.microsoft.com/en-us/
library/wc500chb(v=vs.80).aspx)
Later I did more research and found out that JavaScript has with
statement, too. But there is a difference between them. Article "with
Statement Considered Harmful" by Douglas Crockford (http://
www.yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/)
states that there is a problem with variable scopes, that is, if
object does not contain method or field requested, with statement will
manipulate global variable with the same name. This problem (as far as
I can see) is absent in VB, because when manipulating object's
methods or fields it is accessed using a dot (e.g. 'foo = false' in JS
and '.foo = false' in VB). This syntax even makes it possible to nest
something like for statement into with statement, not worrying if
there is a field called for .
With that being said, I would like to propose a new (not that I like
to be seperatist or something like that) syntax for the problem of
method cascading. Best way to show it is using examples

//CASCADES
foo.{
a = 42,
b(foo.a),
c.d = 7,
e.{a = b, c = d}
};

//WITH STATEMENT
with (foo){
.a = 42;
.b(.a);
.c.d = 7;
with (.e){
.a = a;
.c = d;
}
}


//CASCADE
class Node {
String key;
Node(this.key);
Node left;
Node right;
}

void main() {
Node right = new Node('e');
Node root = new Node('root').{
left = new Node('a').{
left = new Node('b').{
left = new Node('c')
},
right = new Node(‘d')
},
right = right
};
print(root);
}

//WITH STATEMENT
void main(){
Node right = new Node('e');
Node root = new Node('root');
with (root) {
.left = new Node('a');
with (.left){
.left = new Node('b');
with (.left) {
.left = new Node('c');
}
.right = new Node('d');
}
.right = right;
}
print(root);
}
root // Someone have mistake here either me or author of original
paper.
/ \
a e
/ \
b d
|
c

In my opinion this is more readable and familiar: no commas, just
semicolons and no semilocolons after brackets. It reduces confusion
(and probably mistakes) if we do not have some new rules when we have
to use commas instead of semicolons. (Notice that there are no
semicolons after with block.)
I should mention that with statement would not resolve open questions
mentioned in original paper.

John

unread,
Nov 24, 2011, 4:34:20 PM11/24/11
to General Dart Discussion
Support for extension methods means inherently supports method
chaining.

So I would make an extension method like so:

static int countWords(this String anyString, [String delimeter = " "])
{
...
return wordCount;
}

Then I have

String x = "now is the time";

x
.countWords()
.isEven(); //or whatever. any extension that operates on bool could
then continue this chain.


On Nov 23, 8:19 am, John <pruj...@gmail.com> wrote:
> +1
>
> I would advocate support for what .net has implemented along these
> lines:  extensionmethods.
>

> If dart supportedextensionmethods, then chaining would be naturally


> supported.
>
> John
>
> On Nov 21, 7:48 pm, Bob Nystrom <rnyst...@google.com> wrote:
>
>
>
>
>
>
>
> > Jacob Richman, one of the people feverishly working on our new DOM library
> > has a language proposal to share with you. You can see it in Google doc
> > form here:https://docs.google.com/document/d/1U0PeHtVQHMQ8usy7xI5Luo01W5LuWR1ac...
> > .
>
> > One of the questions that's come up a few times with our DOM API is whether
> > or not it supports jQuery-style chaining. This proposal is answer to that.
>
> > jQuery is one of the most well-known examples of APIs designed around a
> > fluent interface. StringBuffer is another. EaselJS (http://easeljs.com/)
> > tries to make HTML Canvases easier to use by wrapping them in this style.
>
> > But note that all of those APIs had to be designed to specifically work

> > with that pattern. Also note that they work withmethods, but not getters

Dirk Detering

unread,
Nov 25, 2011, 3:44:17 AM11/25/11
to John, General Dart Discussion
Hello John,

2011/11/24 John <pru...@gmail.com>

Support for extension methods means inherently supports method
chaining.

Right. This would allow for an Fluent-Interface style like was shown for
JQuery in this thread:

> >   $('#myTable')
> >       .find('.firstColumn')
> >           .css({'background': 'red',
> >                 ‘border’: ‘2px solid black’})
> >           .text(‘first column’)
> >       .end()
> >       .find('.lastColumn')
> >           .css('background','blue')
> >           .text(‘last column’);

I see three problems for this approach in comparision to the cascade proposal:

1.) Extension methods are a style to dynamically add behaviour to a type.
     I.e.: To enhance a type API(!) for the current application, not to add methods
     which contain domain logic.
     Doing the latter would be an abusive manner.
     So I would like to see where you land when you base your extension method
     example not on countWords and isEven but on the

2.) According to the problem at hand, extension methods would only allow for
     adding fluent interface behaviour to the types.
     Related to the JQuery example above you would end up with a good bunch
     of extensions for quite some types AFAIC. 
     But if the types are meant to be used as tree based API, then the fluent interface
     would better be part of the type's behaviour anyway, so no need for extension methods.
     They would complicate things unnecessarly for the typical use case of that types.

3.) OTOH, the fluent interface style based API for this kind of code structure always
     has one problem, i.e. going back one ore more levels up in the element structure.
     As shown in the JQuery example above, you have to introduce .end() to go one
     step back or .end().end() chains to go more than one step back.
     All in all this makes the code much more noisy and less readable than a well
     designed builder approach based on Groovy style closures or like the mentioned
     call cascades, where the structuring is based on the curly braces like anywhere else
     in the program code.

     Beside that, a fluent interface for a dynamic multi-type node structure can be very
     hard to design right. It typically only works if the nodes explicitly know their parents.
     That is, the tree structure must be inherently well maintained and navigable.

     The cascase proposal does not have this problem, as the tree navigation is kept
     outside of the tree structure itsself.  Even if a subnote has no way to navigate back
     to its parent, backtracking to the next higher level would be possible. And that very
     easily over more than one level. Simply add an acccording amount of braces, like
     in a nested loop or if structure.
     
KR
Det


 

John

unread,
Nov 25, 2011, 7:15:54 AM11/25/11
to General Dart Discussion
This is a great discussion by the way. I'm always in favor of healthy
debate. :)

I do concede that EMs are not suitable for the specific case where you
want to move back up through the chain to get to a previous "state" of
the chain itself.

My case for EM's is probably better put in a dedicated thread, so I'll
spend some time later doing that.

On Nov 25, 2:44 am, Dirk Detering <mailto...@googlemail.com> wrote:
> Hello John,
>
> 2011/11/24 John <pruj...@gmail.com>

Daniel Carvajal

unread,
Dec 27, 2011, 3:04:54 AM12/27/11
to General Dart Discussion
In dartexperience can find more information about this

Porfirio

unread,
Dec 28, 2011, 4:47:05 PM12/28/11
to General Dart Discussion
I think that what you want is something like this: http://try.dartlang.org/s/pJkk
It's the way jQ does it.

Dont blow the language, or add some dsl like http://en.wikipedia.org/wiki/QML

Dirk Detering

unread,
Dec 29, 2011, 4:39:07 AM12/29/11
to Porfirio, General Dart Discussion

Porfirio, this exact matter has been discussed before in this thread. Please read it completely before trying to answer.
http://groups.google.com/a/dartlang.org/group/misc/browse_thread/thread/611c04100ac17142/6832dc5c4425c363?lnk=raot#6832dc5c4425c363

Reply all
Reply to author
Forward
0 new messages