July 2009 Archives

Too much Perl 6

| No Comments | No TrackBacks

So, yesterday I was giving a quick perl workshop using Catalyst. The idea was to write a blog in 3 hours. At some point I wrote the following code:

sort { (stat $_)[10] } glob 'posts/*';

And it didn't work, because Perl 5 doesn't DWIM when I have a sort routine that takes only one parameter, while Perl 6 would realize that and use that value for a later sorting. Basically, Perl 6 implemented the schwartzian transform in its core.

Epitáfio

| No Comments | No TrackBacks

The Perl monks from Brazil accepted a challenge with an important social relevance. We are working, on our spare time, in the development of a system to manage the public cemeteries. Few people know, but the public cemeteries take a fundamental role regarding the respect for human rights, as well as for public health, giving the population that doesn't have the resources to pay for a tombstone in a private cemetery a memorial service and a decent burial.

At this moment we already stablished the features for the first release, and the deadline expectation is that we have the system working in the cemetery for the "dia de Finados" (day in memory of the people who past away). Yesterday we finished the first proposal for the data model (the software will be developed in Portuguese, but I guess you can figure out the meaning of the words).

The discussions regarding the system happen in the #brasil-pm channel at irc.perl.org, we have a space in the perl.org.br wiki to document the development process and a github space to host the source code.

The system will have a Web interface and is going to use PostgreSQL, specially because of the timestamp-related features and also for possibly using PostGIS, allowing to store the spatial information about the tombstones and the cemetery map.

As the development follows, I'll post the updates here.

I started looking at SOAP around 2001, and at that time SOAP::Lite was the only viable option to both consume and produce SOAP services in Perl. Everybody knew SOAP::Lite was a mess, but it was pretty much what we got and we were stuck with it.

In 2001 SOAP::Lite wasn't much a problem and that's because it implements the RPC/Encoded format for SOAP messages, and by that time this was pretty much the default. But the usage of SOAP changed, a lot, and the module didn't catch up with that changes.

First of all, we need to understand that SOAP is much more than a way to do RPC, if you need plain RPC without much management, then XML::RPC or JSON::RPC is just fine and will work without any concerns. But if, on the other hand, you need a strict management of the data being transfered as well as a proper documenting of which services use which data types, then SOAP is probably the thing you need.

But for using SOAP, there are two aspects you need to undestand:

Style

The style describes the general semantics of the service. There are two different styles in SOAP:


  • Document: This style is used for services that represent the submission of some arbitrary data that expects a result or not. One example of a service where Document style makes sense is to submit a purchase in a B2B system.
  • RPC: This style is used where you're acessing a procedural or oriented object API of some sort, its semantics define that one specific resource support several operations.

The biggest practical difference is that in the Document Style, you're going to submit one or more XML documents as the message, and this document is the resource to be used by that specific operation. In the RPC style, the first and only child of the body element describes which is the operation that this message is trying to execute, and the parameters for that operation are the direct child of that main element.

Body Use

Indpendent of the style of the service, the other aspect that governs how SOAP works is the "use" of the body. There are two types of "use"


  • Encoded: This referes to SOAP-Encoding, which is a specific format for data serialization described by the SOAP protocol itself. In the early usage of SOAP, this was the main way of exchanging data.
  • Literal: This defines that the content of the message is actually encoded acording to some arbitrary XML Schema, this has the advantage of being able to represent any data that XML can, and also makes the serialization format more decoupled from the language providing the service.

Style/Use

Considering all that, there are four ways of using SOAP:


  • RPC/Encoded: This is the only format supported by SOAP::Lite, and hardly used in new services.
  • RPC/Literal: This allows you to use arbitrary XML data as the parameters and response for an API that you want to expose.
  • Document/Encoded: While this mix is theoretically possible, I have never heard about any use of it.
  • Document/Literal: This is a type of service where you have only one operation per endpoint, and usually don't map well to RPC semantics, since it would require several endpoints to implement the whole API.

WARNING: Microsoft created a pseudo-standard called Document/Literal-Wrapped which is a mix of both RPC/Literal and Document/Literal. The service is described with Document/Literal, but the XML Schemas of the service are made considerably more complex to specify the wrapper element that would be otherwise natural in the RPC Style. It also requires the use of the SOAP-Action header to define which operation is being called. The SOAP-Action header is HTTP specific and was supposed to be used for routing purposes, not to define which is the operation being invoked. Please use RPC/Literal when you need RPC semantics with Literal body use, although I have implemented a compatibility mode for this aberration, it should not be promoted in any way.

Finally, let's implement some SOAP.

The first thing you need to do when implementing a SOAP service is deciding wether your service has RPC or Document semantics. In our example, I'm going to implement a Document-style service "converter", which receives a document describing the amount, the origin unit and the target unit, returning a document with the target amount set.

An example of a RPC style service would be an API that would include things like: get_available_source_units(), get_available_target_units() and other correlated operations. In our case we are going to assume that the document itself provides all the information we need and that the associated metadata (the XML Schema) will provide us sane data.

The second think you need to do is describing your data in the XML Schema format, so for our "unit converter" service, we're going to have a UnitConversion element. If you're used to XML, you know that you need a namespace URI for your data and your services, I'm going to use http://daniel.ruoso.com/categoria/perl/soap-today as the namespace for both the data and the service.


<xsd:schema elementFormDefault="qualified"
xsd:targetNamespace="http://daniel.ruoso.com/categoria/perl/soap-today"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:unit="http://daniel.ruoso.com/categoria/perl/soap-today">
<xsd:element name="UnitConversion">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="input">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="amount" type="xsd:double" />
<xsd:element name="unit" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="output">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="amount" type="xsd:double" />
<xsd:element name="unit" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>

The above XML Schema will support data that looks like:


<UnitConversion>
<input>
<amount>10</amount>
<unit>kg</unit>
</input>
<output>
<unit>pound</unit>
</output>
</UnitConversion>

Now that you specified how your data looks like, you need to specify how your service looks like that is done using the Web Service Description Language -- WSDL. Basically, you define:


  • Messages: The messages describes a single message exchange, independent of the Message Exchange Pattern. The WS-I says that you should have "Request" and "Response" as part of the message names, but I disagree, since a message might be used for both request and response.
  • Port Type: The Port Type describes the interface of the service, independent of the transport, basically grouping the messages into a specific "Message Exchange Pattern" -- usually this is request/response, but you can have both request-only and reponse-only.
  • Binding: The binding associates a given port type with some transport mechanism and describes the attributes of how that message will be transported. It's in the binding that you'll describe you want to use SOAP as well as the style/use.
  • Service: The service groups a set of bindings defining where are the endpoints for the client to access the services.

I'm not going to explain each detail of the WSDL here, but I think it's pretty straight forward. WSDLs are complex when generated by tools like .Net, but it doesn't need to be. This WSDL assumes that the previous XML Schema is saved as unit-conversion.xsd.


<wsdl:definitions
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:unit="http://daniel.ruoso.com/categoria/perl/soap-today"
targetNamespace="http://daniel.ruoso.com/categoria/perl/soap-today">
<import namespace="http://daniel.ruoso.com/categoria/perl/soap-today"
uri="unit-conversion.xsd" />
<wsdl:message name="ConvertUnit">
<part name="UnitConversion" element="unit:UnitConversion" />
</wsdl:message>
<wsdl:portType name="ConvertUnit">
<wsdl:operation name="convert_unit>
<wsdl:input message="unit:ConvertUnit" />
<wsdl:output message="unit:ConvertUnit" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="ConvertUnitSOAPHTTP" type="unit:ConvertUnit">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<wsdl:operation name="convert_unit>
<wsdl:input>
<soap:body use="literal">
</wsd:input>
<wsdl:output>
<soap:body use="literal">
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="ConvertUnitService">
<wsdl:port name="ConvertUnit" binding="unit:ConvertUnitSOAPHTTP">
<soap:address location="http://localhost/myservice" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

Ok, now let's really implement it

Now that we're gone through all the overhead of SOAP (and I say it again: if you think this overhead is overkill, go use XML::RPC or JSON::RPC instead, but if you need the services to be strictly documented, SOAP is what you need) we can implement the service itself. And for that we're going to use Catalyst::Controller::SOAP.

I'm not going to explain how to create a Catalyst application, there are lots of tutorials on how to do it, and if you're still reading this, you're probably aware of Catalyst already. So, after you create your Catalyst application, you need a controller that will implement the service, all you need to do is subclass Catalyst::Controller::SOAP and implements the service itself.


package MyApp::Controller::UnitConverter;
use strict;
use warnings;
use base qw(Catalyst::Controller::SOAP);

__PACKAGE__->config->{wsdl} =
{wsdl => '/usr/share/unit-converter/schemas/UnitConverter.wsdl',
schema => '/usr/share/unit-converter/schemas/unit-conversion.xsd'};

sub convert_unit :WSDLPort('ConvertUnit') {
my ($self, $c, $unit_conversion) = @_;
my $data = $unit_conversion->{UnitConversion};
if ($data->{input}{unit} eq 'kg' &&
$data->{output}{unit} eq 'pounds') {
$data->{output}{amount} =
2.20462262 * $data->{input}{amount}
$c->stash->{soap}->compile_return($unit_conversion);
} else {
$c->stash->{soap}->fault({ code => 'SOAP-ENV:Client', reason => 'unsupported' });
}
}

1;

And voilá! You have a SOAP service running. Please refer to the Catalyst::Controller::SOAP docs for more information on the details or just ask me, either in the comments or at #catalyst@irc.perl.org

Dice Game Perl 6

| 3 Comments | No TrackBacks

Following SF, I thought I could present an interesting solution to the dice game as in If you only had one programming language to choose –or– Let the FUD be with you.

SF did rewrite the same algorithm in Perl 6, but I thought I could give a more Perl 6 approach to the problem, leading to the following code:

sub dice($bet, $dice) {
  given $dice {
    when * <=  50 {          0 }
    when * <=  66 {       $bet }
    when * <=  75 { $bet * 1.5 }
    when * <=  99 {   $bet * 2 }
    when * == 100 {   $bet * 3 }
  }
}
sub MAIN($bet, $plays) {
  my $money = 0;
  $money += dice($bet, int(rand() * 100)+1) for ^$plays;
  say "{$bet * $plays}\$ became $money\$ after $plays plays:
     You get {$money / ($bet * $plays)}\$ for a dollar";
}

Let's go through the code step-by-step...

sub MAIN

This is a very handy thing that comes in in Perl 6 natively, if you declare a signature to a specially named subroutine MAIN, this signature will be used as GetOpt instructions, in the code above I asked for two positional arguments, which would mean two parameters:

perl6 dice.pl 40 100

But I could also ask for named parameters and it would require named command-line switches. Very handy.

for ^$plays

The prefix:<^> operator, when used with a number, generates a Range from 0 to that number - 1, so, it would be the same as 0..($plays - 1), but as the number of the play is not important here, it would have the same effect as 1..$plays... Very handy too.

"{$bet * $plays}"

Quotes in Perl 6 are clever, you can open a bracket and type in an expression that will be evaluated.

when * <= 50

This is the Whatever in action. It will generate a closure that will ask for one parameter, when knows about it and sends the "given" value to it. Very handy indeed.

The Perl vs DarkPAN issue is evolving a lot these days, I'd like to explore a viable option so we get out of this mess.

Let me just review which are the options that are clearly not acceptable by one side or the other:


  • Have the defaults for Perl 5 changed, in a backward-incompatible way, favoring Modern Perl
  • Enforce backward-compatibility in order to preserve the huge amount of software running in Perl around the world.

It seems pretty clear that going to each extreme will not build any consensus, so, I was thinking about the way "perl -E" works on 5.10, and I was wondering if we could do one of:


  • Have the defaults changed, but with a switch that would enable "backward-compatible mode"
  • Have the defaults kept, but with a switch that would enable "modern-perl mode"

Basically, what I'm proposing is that we assume that there are two Perls in perl, and by a switch you can select the one you're going to work with. Even if the "modern-perl mode" is assumed to be default, simply adding a switch to the invocation of the interpreter in the old systems wouldn't be much hard.

NPEREZ has written that we shouldn't care about the DarkPAN, Both AdamK and SF had argued about it, but I'd like to take it to another perspective.

One of the key strengths of both Perl and perl, has been the way old code is still supported in the language. That is a decisive aspect as when to choose a language to invest a lot of time, and by that, money. I'll take an example that is not part of the DarkPAN and that has been, just last week, released as free software.

About a year ago, I started working in a software, and of course, I chose to write it in Perl. More than that, I have also made some interesting choices, that including the use of SOAP over XMPP to design the choreographies of the application, as well as the use of Catalyst to implement that. That led me to write some new CPAN modules. Basically I was using XML::Compile, XML::Compile::Schema and XML::Compile::SOAP as the foundation to the entire application.

As the software evolved, a new major release of the XML::Compile framework was released, and it mostly changed the API and a lot of default behaviors. This change costed me about 3 months in investigation and patches, both to my code, to XML::Compile's code and to some other modules just to have the code working again.

Imagine what happens if the defaults for the core changes? That would represent an incredibly higher number of issues to be dealt with, and we don't actually have a choice of "sticking with the old perl", because the developers usually are not the sysadmins, and having to convince a sysadmin that you need to install an old, unsupported version of a CPAN module is already bad, imagine having to convince a sysadmin to install an old, unsupported version of perl? and by that it would mean to keep an entire module tree specific for that version?

That would certainly be a reason for a manager to decide to switch over from Perl to something else, and a very good one. In fact, one of the key strengths of Perl is precisely the long backward compatibilty. And that is not only important only to the DarkPAN, but for every single piece of legacy code in the free software ecossystem as well.

About this Archive

This page is an archive of entries from July 2009 listed from newest to oldest.

June 2009 is the previous archive.

August 2009 is the next archive.

Find recent content on the main index or look in the archives to find all content.