At the early stage of development of Mongo DB Perl 6 driver I stepped into the same problem that every language solved differently - how to separate Wire document(s) from Wire options.
For example
- Python flattens everything in huge messy list of params
insert(doc_or_docs[, manipulate=True[, safe=False[, check_keys=True[, **kwargs]]]])
find([spec=None[, fields=None[, skip=0[, limit=0[, timeout=True[, snapshot=False[, tailable=False[, sort=None[, max_scan=None[, as_class=None[, slave_okay=False[, await_data=False[, partial=False[, manipulate=True[, **kwargs]]]]]]]]]]]]]]])
- Perl 5 is inconsistent, because it sometimes flattens
$coll->insert({ name => 'mongo', type => 'database' }, {safe => 1});
and sometimes chains
$cursor = $coll->query->limit($per_page)->skip($page_num * $per_page);
making it hard to understand type of object returned and when actual query to database takes place.
- Perl 6 gives tons of new capabilities when it comes to method signatures, overloading and dispatching. Logic behind my idea of interface is that I want to keep simple things simple.
For example $collection.insert( %document1, %document2 ) is the most natural way of inserting multiple documents (note that Perl 6 does not flatten it the same way as Perl 5).
However slurpy parameters must appear last, and that would require placing options before them
$collection.insert(:continue_on_error, %document1, %document2) # note the bool flags in Perl 6
or giving up on them
$collection.insert( [%document1, %document2], :continue_on_error)
and this array wrapping I personally dislike.
In my humble opinion closures are the way to go. Some examples:
$collection.insert(:continue_on_error).(%document1, %document2)
or without options
$collection.insert.(%document1, %document2)
or reuse closure
my $feeder = $collection.insert(:continue_on_error);
$feeder.(%document1, %document2)
$feeder.(%document3)
my $cursor = $collection.find(limit=>20, fields=>['name', 'age'] ).()
yes, this is correct, many problems are solved if cursor object instance is returned from closure, not directly from collection
$collection.find.(nick=>'bbkr')
throwing options to closure removes nasty hash wrapping here, this is my nice-to-have
Benefits:
- closures are fast, whole Perl 6 is closure based
- closures allow to "BSONize" some parts of Wire protocol fields and reuse them changing only %document(s)
- your query is executed when closure is executed, clear rules on that
Problems:
- many users will forget to call closure, but since we have awesome where{} blocks in subroutine signatures we can detect $collection.insert( %document1, %document2 ) case and assume one wants $collection.insert.( %document1, %document2 ) with very small performance impact, beginners will be happy, hackers can still use Wire options.
Please share your thoughts.