Haskell diary #4

I’m learning functional programming and writing things down as I go. Some of my understanding may be incomplete or wrong, and I expect to refine it in the fullness of time.

Yesterday I worked on CIS194 Homework 3. I started to question my approach of trying to write code first and reading up on Haskell features as necessary. It’s proving to be quite challenging. Perhaps working through a book first would be more reasonable.

Pattern matching on constructors proved to be challenging to wrap my head around. I found it strange that the compiler has enough information to figure out exactly how a value was constructed.

However, once I got a bit comfortable with it, I was able to write code like this:

where sortedMessages = sortMessages [ m | m <- ms, highSeverity m ]
      highSeverity (LogMessage (Error sev) _ _) = sev >= 50
      highSeverity _ = False

I also found myself using a few list comprehensions like the above. They provide convenient syntactic sugar to combine map and filter:

[ show m | m <- ms, isInfixOf (lowerCaseText m) (lowerCase s) ]

I had to parse strings like "E 2 562 help help" in this exercise. I ended up writing a bunch of nested case expressions:

case first of 
    "I" ->
        case readInt second of
            (ValidInt n) -> ValidLM $ LogMessage Info n (unwords (third : rest))
            InvalidInt -> InvalidLM s
    "W" ->
        case readInt second of
            ValidInt n -> ValidLM $ LogMessage Warning n (unwords (third : rest))
            InvalidInt -> InvalidLM s
    "E" -> 
        case readInt second of 
            InvalidInt -> InvalidLM s
            ValidInt n -> 
                case readInt third of
                    InvalidInt -> InvalidLM s
                    ValidInt m -> ValidLM $ LogMessage (Error n) m (unwords rest)
    _   -> InvalidLM s

This is clearly ugly (similar I and W cases, repeated InvalidLM construction), but so far I’m not sure how to make it better. I have a feeling more pattern matching could be in order.

I learned a couple of other things. One is that pattern matching can also be used in lambdas:

map (\(ValidLM m) -> m) (filter f ms)

The other is that I could write destructuring assignment like this:

where (first : second : third : rest) = words s

Even though this worked, I’m not sure whether the left hand side should really be a list, or whether a tuple would make more sense.