Underscore for Dart + Mixins

306 views
Skip to first unread message

Demis Bellot

unread,
May 1, 2012, 12:27:32 AM5/1/12
to mi...@dartlang.org
For anyone that's interested I've just published a port of Underscore.js for Dart at: https://github.com/mythz/DartMixins
It includes all the utils from http://underscorejs.org (and some string functions) that could be ported over (without requiring Reflection).

Because the '_' is a special char in Dart, I've had to use '$'. Here are some examples: 

$([1,2,3,4,5]).first(3) -> [1,2,3]
$([1, 2, 3]).reduce((sum, num) => sum + num, 0)  -> 6

It also provides an extensible "Mixin-like" API, (the best I can provide without Dart having any built-in support for Mixins itself):

Mixin.mixin({
  'myReverse': (string) => $( $(string.splitChars() ).reverse()).join('')
});
 
$("test").myReverse() -> tset

You can also provide a more typed api and inject your own implementation by inserting hooks into the '$()' function call e.g:

class MyListExtensions extends List$ {
    MyListExtensions(target) : super(target);
    double avg() => sum() / target.length;
    int count(predicate) => filter(predicate).length;
}
Mixin.registerFactory((x) => x is List ? new MyListExtensions(x) : null);
 
$([20,30,50,100]).count()  -> 4
$([20,30,50,100]).avg()    -> 50

I personally think not having Mixins support built-in is currently Darts biggest limitation (next to Reflection). 
In https://github.com/mythz/DartMixins#the-need-for-mixins I describe the issues Dart currently suffers from by not having Mixins, namely adding un-necessary functions on an interface impose un-necessary burden on all its implementers, instead of being able to provide 1 implementation that can apply to all of them.

I don't think the current convention of plurazing the Interface type is a a great convention since the core Dart library 'takes' the most obvious name but provides very little functionality in return, e.g The http://api.dartlang.org/dart_core/Strings.html and http://api.dartlang.org/dart_core/Futures.html classes only have 3 static methods between them, but prevents others from creating static utils with the same name.

Anyway failing built-in support for Mixins, I think this API is the next best thing by providing additional functionality to built-in types with a familiar, terse and intuitive API.

Hope it proves useful :)


--
- Demis


Seth Ladd

unread,
May 1, 2012, 12:40:20 AM5/1/12
to Demis Bellot, mi...@dartlang.org
Thanks for sharing! You might be interested in the new "method cascade" functionality that will be added soon. Here's the bug for the VM and one for the Frog compiler and one for the "meta bug".  The dart2js compiler already supports method cascades.

Using cascades, you'll be able to do this:

$([1,2,3,4,5]).first(3)..sum()

mythz

unread,
May 1, 2012, 12:52:49 AM5/1/12
to General Dart Discussion
Thanks Seth,

Yep already knew about up-coming method cascades support (it was the
major reason why I decided not to return a wrapped instance :)
Unfortunately it was still yielding a syntax error in the latest
DartEditor (Build 6943) so didn't know how long it was going to take
to be implemented - glad to hear it's in dart2js already :)

Looking forward to it, should make the code a lot more fluent!

Cheers,
Demis


On May 1, 12:40 am, Seth Ladd <sethl...@google.com> wrote:
> Thanks for sharing! You might be interested in the new "method
> cascade<http://news.dartlang.org/2012/02/method-cascades-in-dart-posted-by-gi...>"
> functionality that will be added soon. Here's the bug for the
> VM<http://code.google.com/p/dart/issues/detail?id=2501>and one
> for the Frog compiler
> <http://code.google.com/p/dart/issues/detail?id=2843>and one for the
> "meta
> bug <http://code.google.com/p/dart/issues/detail?id=2844>".  The dart2js
> compiler already supports method cascades.
>
> Using cascades, you'll be able to do this:
>
> $([1,2,3,4,5]).first(3)..sum()
>
> On Mon, Apr 30, 2012 at 9:27 PM, Demis Bellot <demis.bel...@gmail.com>wrote:
>
>
>
>
>
>
>
> > For anyone that's interested I've just published a port of Underscore.js
> > for Dart at:https://github.com/mythz/DartMixins
> > It includes all the utils fromhttp://underscorejs.org(and some string
> > functions) that could be ported over (without requiring Reflection).
>
> > Because the '_' is a special char in Dart, I've had to use '$'. Here are
> > some examples:
>
> > $([1,2,3,4,5]).first(3) -> [1,2,3]
> >> $([1, 2, 3]).reduce((sum, num) => sum + num, 0)  -> 6
>
> > It also provides an extensible "Mixin-like" API, (the best I can provide
> > without Dart having any built-in support for Mixins itself):
>
> > Mixin.mixin({
> >>   'myReverse': (string) => $( $(string.splitChars() ).reverse()).join('')
> >> });
>
> >> $("test").myReverse() -> tset
>
> > You can also provide a more typed api and inject your own implementation
> > by inserting hooks into the '$()' function call e.g:
>
> > class MyListExtensions extends List$ {
> >>     MyListExtensions(target) : super(target);
> >>     double avg() => sum() / target.length;
> >>     int count(predicate) => filter(predicate).length;
> >> }
> >> Mixin.registerFactory((x) => x is List ? new MyListExtensions(x) : null);
>
> >> $([20,30,50,100]).count()  -> 4
> >> $([20,30,50,100]).avg()    -> 50
>
> > I personally think not having Mixins support built-in is
> > currently Darts biggest limitation (next to Reflection).
> > Inhttps://github.com/mythz/DartMixins#the-need-for-mixinsI describe the
> > issues Dart currently suffers from by not having Mixins, namely adding
> > un-necessary functions on an interface impose un-necessary burden on all
> > its implementers, instead of being able to provide 1 implementation that
> > can apply to all of them.
>
> > I don't think the current convention of plurazing the Interface type is a
> > a great convention since the core Dart library 'takes' the most obvious
> > name but provides very little functionality in return, e.g The
> >http://api.dartlang.org/dart_core/Strings.htmland
> >http://api.dartlang.org/dart_core/Futures.htmlclasses only have 3 static

Ladislav Thon

unread,
May 1, 2012, 4:34:29 AM5/1/12
to Seth Ladd, mi...@dartlang.org, Demis Bellot


> $([1,2,3,4,5]).first(3)..sum()

I'm already able to do this without cascades in Dart Query ( https://github.com/Ladicek/dart-query), which is a collection library not unlike Underscore.

Having every operation return new wrapper allows doing the work lazily, which is a big win (not that it wouldn't be possible with a single wrapper only, using some kind of operation queue, but that's way more involved).

LT

Dirk Detering

unread,
May 1, 2012, 6:11:30 AM5/1/12
to Seth Ladd, Demis Bellot, mi...@dartlang.org
2012/5/1 Seth Ladd <seth...@google.com>

 
Using cascades, you'll be able to do this:
$([1,2,3,4,5]).first(3)..sum()

Sorry, somehow I can't follow here. How would the cascading work (and thus help) here?

As far as I understood cascades, the double dot would implicitly not provide a result but the original receiver.

So if
$([1,2,3,4,5]).first(3) -> [1,2,3]

Then ..sum() would work on [1,2,3] and not $([1,2,3]) - and then provide the same [1,2,3] instead of 6 to another
double-dot method call.

OTOH if I assume the double dot to jump back to the original receiver of the method call it is following,
then ..sum() would be called on the receiver of .first(3), and that is the wrapped [1,2,3,4,5], and not a wrapped
[1,2,3].

So how will the cascade sugar solve the issue here?

Jan

unread,
May 1, 2012, 9:48:35 AM5/1/12
to mi...@dartlang.org
I'm already able to do this without cascades in Dart Query ( https://github.com/Ladicek/dart-query), which is a collection library not unlike Underscore. 

I'm working on my own 'LINQ to Dart' implementation and at first sight it looks dauntingly similar to your library. Thank you, I look like a shameless copycat now :). 

Ladislav Thon

unread,
May 1, 2012, 9:51:44 AM5/1/12
to Jan, mi...@dartlang.org
I'm working on my own 'LINQ to Dart' implementation and at first sight it looks dauntingly similar to your library. Thank you, I look like a shameless copycat now :). 

Not really. Go ahead with your work, it's fun and you'll get some working experience with Dart, which is never bad, even if your library will never be used (mine won't be either, don't worry, it was a learning project for me too :-) ).

LT

Simon Richardson

unread,
May 1, 2012, 10:00:09 AM5/1/12
to Jan, mi...@dartlang.org
On 1 May 2012, at 14:48, Jan wrote:

I'm already able to do this without cascades in Dart Query ( https://github.com/Ladicek/dart-query), which is a collection library not unlike Underscore. 

I'm working on my own 'LINQ to Dart' implementation and at first sight it looks dauntingly similar to your library. Thank you, I look like a shameless copycat now :). 

mythz

unread,
May 1, 2012, 6:08:51 PM5/1/12
to General Dart Discussion
Right, it looks like it will need to mutate the list (instead of being
pure functions) for it to work.
Once method cascades are in I should be able to change it to signal
the wrapper that further invocations should mutate the list as well,
something like:

>> $([1,2,3,4,5]).chain().first(3)..sum()

Not ideal, but should work.

*Real Mixins* in Dart would be preferred, star this issue if you want
to see it happen :)
http://code.google.com/p/dart/issues/detail?id=33

- Demis

On May 1, 6:11 am, Dirk Detering <mailto...@googlemail.com> wrote:
> 2012/5/1 Seth Ladd <sethl...@google.com>
Reply all
Reply to author
Forward
0 new messages