Fluent interfaces in Perl 6
I've been digging into Perl 6 more lately and I noticed the Wikipedia example of fluent interfaces didn't have a Perl 6 example, so I fixed that.
To be fair, Martin Fowler's explanation (as usual) of fluent interfaces does a much better job of explaining them, but a key point is that setters have a return value. For many fluent interfaces, the setters set a value and actually return a new instance of a different object for you to call methods on. Thus, the examples in Wikipedia don't always meet the criteria of a fluent interface, but I added a Perl 6 version that closely modeled the PHP version (but more concisely, and with much better type safety). I sidestepped the entire fluent interface debate.
Here's my example:
class Employee {
subset Salary of Real where * > 0;
subset NonEmptyString of Str where * ~~ /\S/;
has NonEmptyString $.name is rw;
has NonEmptyString $.surname is rw;
has Salary $.salary is rw;
method gist {
return qq:to[END];
Name: {$.name}
Surname: {$.surname}
Salary: {$.salary}
END
}
}
my $employee = Employee.new();
given $employee {
.name = 'Sally';
.surname = 'Ride';
.salary = 200;
}
say $employee;
And that prints:
Output:
Name: Sally
Surname: Ride
Salary: 200
The lovely folks on the #perl6 IRC channel helped me fix a few misunderstandings (they're really awesome), and once again, I find Perl 6 is just a joy to work with.
Cool!
One remark: you don't need a closure for showing an attribute in a string, so:
qq/Name: $.name/
will just do fine!
How would you write Martin Fowler's JMock example (which you linked to) in Perl 6?
+1
Is there a way to get perl6 to generate an error message if a grammar does not match? Or at least return the position of the last data it processed? It is quite hard to fix syntax errors if all I get from the parser is 'no match'.
I'm not fluent in Perl6, but I don't see any fluent interface happening here. I'd expect something like
E. Choroba:
There are two problems with that statement. First, the Wikipedia page shows method chaining and not really "fluent" interfaces. Second, you're used to method chaining as a way of making some code more readable. Perl 6 uses different idioms and the
given
idiom above is more natural for the language.http://stackoverflow.com/questions/19618287/perl6-grammars-error-reporting
Ovid - cool on you for taking the time to spread Perl6 knowledge, especially on such an important site as Wikipedia - keep up the good job :)
However...
"Perl 6 uses different idioms and the given idiom above is more natural for the language."
If that's really the case it would personally make Perl6 a pain to read and I hope (again personally, YMMV) that it's not true. Sorry :(
say $employee.name('Sally').surname('Ride').salary(200);
Seems natural to me and forces me to understand nothing about the internal structure of $employee (only the external API).
"given" is a flow control structure. Using it the way you have feels disconnected to me, there's no visible connection to the following "say" statement. Also you are directly setting attributes, rather than using a public API - isn't that usually something bad in OO code?
I like the general direction Perl6 took but some of the details, such as over-reliance on sigils (more line noise) really grate at me. If directly accessing object internal variables instead of using setters is now considered natural/best-practise in Perl6... I don't know... Maybe I don't understand real programming but I know what I like and this, I don't.
Offer, we can safely set public attributes here because Perl 6 makes it trivial to allow powerful subsets which are more or less "the data types you want", but created on the fly.
Also, you can see an older version I did (with Jonathan's help with the MOP hackery) at this older version of that.
You'll note that you can use your version, but if you want it multi-line, you have to end each line with a backslash (or else Perl 6 will think that
.foo
is being called on$_
instead of the invocant which was returned).However, to fully take advantage of a "fluent" interface, we'd have to sit down and understand what users really need, what objects each method should return and write proper methods instead of the cheap method chaining hack. I was having enough fun with the Perl 6 that I should have paid more attention to the fact that what I wrote wasn't fluent (nor are most examples on that page).
Attributes in the interface don’t have to translate to fields in the class and assignments to such attributes don’t have to mean unchecked writes to private fields. It’s trivial to provide the same attribute interface outwardly but actually implement it with accessors/mutators behind the scenes.
Just because that code would be bad in Java doesn’t mean it’s bad in Perl 6.
Great news, I love Perl. Thanks a ton