A blog about Modern Perl, bioinformatics and anything else that I feel like rambling about.

Showing posts with label bioperl. Show all posts
Showing posts with label bioperl. Show all posts

Monday, June 22, 2009

Do What I Mean: Moose Types and Type Coercions

API's should be simple. I hate it when a module that solves a non-trivial problem requires the user to make non-trivial decisions about every single detail in the domain problem.

In my opinion, a class should be smart enough to make all the reasonable assumptions so as to require the least possible amount of input from the user.

This might seem a little dangerous, and can certainly be if the approach is taken too far (IO::All is for some people an example of too a DWIMmy API), but there's a healthy middle-point in which the user is not only able to rely on the module to solve the problem at hand, but is also spared of most of the cognitive load that solving that problem requires.

It is very probable that someone looking for an already-cooked solution in the CPAN is not only not willing to code it up for himself, but also doesn't know enough about the problem to do so (at least not initially). This person is going to rely on the module's wisdom, and the least that it's asked of him, the better.

One of the modules that I'm working on deals with protein sequence optimization using genetic algorithms. The user has a collection of protein sequences phylogenetically related, and wants to produce an optimized sequence for a custom trait (solubility, hydrophobicity, digestibility, etc) that still belongs to the original protein family.

Under the hood, the algorithm that I implemented requires a multiple protein alignment as input, or profile. Naturally, the methods that do the heavy-lifting expect a Bio::SimpleAlign object. But the complication is that protein alignments can come in lots of different formats, many of which are also shared with plain protein, RNA and DNA file formats. Also, the user actually shouldn't be aware that the module requires a protein alignment. Of course it should be allowed to provide one, but if the only thing he has is a bunch of sequences in a flat file, it shouldn't be bothered with opening (how?), parsing (what format? What is its specification?) and aligning (with what algorithm? Gap penalty who?) them to cater to my particular implementation. All he should need to give is a simple filename as a string.

So to maximize for user convenience, I decided that the module should accept either of:
In the case that there is ambiguity about whether the user supplied an alignment file or a sequence file (eg., fasta format is both an alignment and a sequence format), I'll make an educated guess and assume that it's an unaligned sequence. In the worst case scenario, It'll just realign an alignment. There is also an extra layer of guessing involved in determining what the format actually is in case that the file has an unknown extension or no extension at all (this is done by Bio::Tools::GuessSeqFormat).

All of this adds to the simplicity of the API in detriment of the simplicity of the underlying code. Luckily, Moose has the tools to make this as straightforward and clean as possible, using Types and Type coercions. The coercion map looks like this:



And the code that implements this is simply the following:

What's better is that all these types and type coercions are defined separately in a type library that uses MooseX::Types. They deal with both the input sanity checking and the type coercions. This way, not only this complexity is hidden from the user, it's also hidden from the main application. This is really helpful, since now most of the code in the module's main file describes the class behavior and it's not coupled with nor hidden by the juggling of all the possible user input types and input validation code.

Now future users of this module (most probably only myself) won't have to check the API's documentation that often; whatever representation of a collection of protein sequences they might have will serve as a valid input. I believe this to be a nice design choice.

Monday, May 18, 2009

A sequence translator folder using POE::Component::DirWatch::Object

So, I admit it. I hate coding GUIs. Probably because I don't have much experience doing it, but I hate it nevertheless. So when I bumped into POE::Component::DirWatch::Object and grasped some of the potential it had for kind of replacing a GUI in some simple scripts, I just had to try it.

What POE::Component::DirWatch::Object basically does is keep an eye on a folder, and act upon an event regarding it. For instance, using ::NewFile, it will trigger a user-defined function whenever a file is created in that directory. This might seem old news for the compsci people, but for me it was quite a new concept and I really liked it.

So to test-drive it, I decided that I'd code for a simple script (not too different than the module's synopsis, in retrospect) that would translate any DNA sequence that was created in it. This way, the user would just have to copy or move her desired sequence files to the "translate" (as I very imaginatively called it) folder, and they would automagically translate themselves.

The core of the script is:


Simple, right? Here, $watcher will execute the translate subroutine whenever a new file is created in the /home/brunov/translate folder. The last line is needed to make POE do its magic. The script runs a daemon that never exits, and could be configured to start when you log in to your session.

The translate routine is pretty straightforward. It uses Bio::Seq for the translation part, and Bio::SeqIO for the file reading and writing. What it does is read the input file, translate all the translatable sequences that are in it, and write the results in a temporary file,
which is later used to replace the original one.


That's all that is needed (minus a little error checking that I chose to leave out for the sake of bloggability).

Adding neat notifications

The script runs fine as it is, but it's kind of dull. Since there are no newly created files nor visible output, you are tempted to reopen the translated file to actually check that your DNA has been translated into protein. This is why I thought that adding system notifications would be appropiate.

Actually, bullshit. I just can't get enough of Ubuntu's sexy notification system, so I figured it'd be nice if a bubble popped up saying the outcome of the translation.

So doing some more CPAN digging, I found Gtk2::Notify. With a few extra lines of code, I got a chatty, ribosomy (get it?) folder. Here's the modified translate sub (minus old comments) and the new notify one:


And that's it! Now whenever you drop a file in the "translate" folder, all DNA sequences that are in it get translated (and those who are not are left untouched). This could allow for cute chained actions; one could have a whole hierarchy of live folders with things such as "reverse translate", "blast it", "predict solubility", "digest it" and so on. It is certainly not as flexible as good old CLI scripts, but it reaches a nice compromise between the user friendliness of a GUI and the batch processing capabilities of command-line fu.

Here's how it looks:



You can see the real-life script here.

P.S: And just to spite TIOBE people: Perl Programming rocks! And also Euphoria Programming and SuperCollider Programming, of course.