I'm a bioinformatician and mainly use python, but I'm trying to learn Haskell. So far I like this language because it's beautiful and fun, but my Haskell scripts usually end up being much slower than my Python equivalent.
Here is my latest attempt:
-- Define type synonyms for fields in a fastq record.
type Name = String
type Nucleotides = String
type Qualities = String
data FastqRecord = FastqRecord Name Nucleotides Qualities
{-
Show is implemented so that the string representation of a FastqRecord
is actually its fastq formatted representation.
-}
instance Show FastqRecord where
show (FastqRecord name s q) = unlines [name, s, "+", q]
-- Parse lines four by four, ignore the third of each batch.
getFastqRecords :: [String] -> [FastqRecord]
getFastqRecords [] = []
getFastqRecords (l1 : l2 : _ : l4 : ls) = FastqRecord l1 l2 l4 : getFastqRecords ls
{-
We assume that the records come in sorted by sequence.
Successive records having the same sequence are fused,
arbitrarilly keeping the first name, and taking the
highest quality at each position.
-}
fuseFastqRecords :: [FastqRecord] -> [FastqRecord]
fuseFastqRecords [] = []
fuseFastqRecords [r] = [r]
fuseFastqRecords (r1@(FastqRecord n s1 q1) : r2@(FastqRecord _ s2 q2) : rs)
| s1 == s2 = fuseFastqRecords (FastqRecord n s1 (bestquals q1 q2) : rs)
| otherwise = r1 : fuseFastqRecords (r2 : rs)
{-
For each position, we want to keep the highest quality
among the records that had the same sequence.
In fastq format, qualities are integers represented as ascii characters
Using max on Char does what we want.
-}
bestquals :: Qualities -> Qualities -> Qualities
bestquals = zipWith max
main :: IO ()
main = interact (concatMap show . fuseFastqRecords . getFastqRecords . lines)
On very small test input, this scripts runs 10 times faster than my python implementation.
On "real" input, the python code (using pypy) uses much more ram than the Haskell version, but runs in half the time.
If I understood my readings so far, Strings in Haskell are not implemented efficiently, so I guess I would have to use another type to process my text.
But the representation as a list of Char is actually very convenient for one of the operations I want to do (see the comments about the bestquals
function).
What would you recommend to do?