Search code examples
haskellfold

Fold a partially applied value constructor over a list?


I have a type like this:

data MessageType = Debug | Error deriving (Show, Eq)

type Code = Int

type Info = String

data Message = Message MessageType Code Info

and a function that takes a string and returns a Message:

parseMsg :: String -> Message

So far I've written a rough state machine:

parseMsg line = fold (words line) Message
  where fold ("D":xs) msgCtr = fold' xs (msgCtr Debug)
        fold ("E":xs) msgCtr = fold' xs (msgCtr Error)
        fold' (code:xs) msgCtr = fold'' xs (msgCtr (readCode code))
        fold'' rest msgCtr = msgCtr (unwords rest)
        readCode code = read code::Int

But I'd rather do this in an actual foldl or foldr.

foldl (\msgVConstructor el -> msgVConstructor el) LogMessage (words line)

The idea is to fold a function (Message -> String -> Message) so that the accumulator, Message is partially applied across the words in the string. However, I get this error

Constructor ‘Message’ should have 3 arguments, but has been given none**strong text**

I'm taking this to mean that a value constructor can't be partially applied in a fold. Is there no way to do this?


Solution

  • Well if I understand it correctly, you basically want to parse three arguments in a constructor. This is not really what a foldr or foldl is supposed to do. foldr basically is a catamorphism over a list. You replace the (:) with f, and the [] with the initial accumulator x0. But since the number of elements in a list is not known at compile time, the type of f always need to have the same type: a -> b -> b with a the type of elements in the list, and b the type of the output of the fold.

    If we however take a closer look to the code you provide, this does not look like a fold at all. You basically have a constructor with three elements, and aim to convert these into a Message. You can do so by interpreting the arguments separately:

    parseMsg line = Message (mtype st) code rest
        where (st : sc : sr) = words line
              mtype "D" = Debug
              mtype "E" = Error
              code = read sc :: Int
              rest = unwords sr

    We thus unpack the words of the line into st, sc, and sr, and then process the first, second an remaining elements into parameters of the Message (with mtype a function that translates the string into the corresponding MessageType), it reads the second parameter as an Int, and finally generate the rest as unwords sr. These parameters are then used to construct a message.