Question

My work in Haskell comes in the form of re-working .Net F# projects in Haskell for the fun of it.

I'm parsing a regular Windows configuration file--one key/value pair per line, key separated from value by =. This file is very simple and straight-forward, which allows my parsing code to be simple and straight-forward, which I like.

My question is why partial application is not working on the last line of code below. It's obviously working in prior lines and other functions.

module Configuration (Config (..), load) where

import Data.Char (isSpace)
import Data.List (isPrefixOf)

data Config = Config { aliases :: [String]
                     , headers :: [String] }

-- This is a naive implementation. I can't decide if I like it better than
-- trim = unpack . strip . pack.
trim :: String -> String
trim = reverse . dropSpaces . reverse . dropSpaces

dropSpaces :: String -> String
dropSpaces = dropWhile isSpace

split _ [] = []
split c cs = [takeWhile (/= c) cs] ++ split c (tail' $ dropWhile (/= c) cs)
  where tail' []     = []
        tail' (x:xs) = xs

load :: String -> Config
load text =
  let ss = lines text
      hs = map getValue $ getLines "Header" ss
      as = split ',' $ getValue $ getLine "AliasList" ss
   in Config { aliases=as, headers=hs }
  where getLines p = filter (p `isPrefixOf`)
        getValue   = trim . drop 1 . dropWhile (/= '=')
        getLine    = head . getLines -- Why isn't partial application working here?

The error I'm getting follows:

Configuration.hs:30:29:
    Couldn't match expected type `[c0]'
                with actual type `[[a0]] -> [[a0]]'
    Expected type: [a0] -> [c0]
      Actual type: [a0] -> [[a0]] -> [[a0]]
    In the second argument of `(.)', namely `getLines'
    In the expression: head . getLines

Thanks!

Was it helpful?

Solution

It is not partial application, but function composition that is not working. You can't pass 2 arguments to a function that is part of a function composition.

OTHER TIPS

The first thing to note is the signature of getLines. Since p `isPrefixOf` has type (Eq a) => [a] -> Bool, getLines p has type (Eq a) => [[a]] -> [[a]] (based on the type of filter). Here [a] seems to be String, so getLines p has type [String] -> [String] and p thus seems to have type String. So in fact, getLines has type String -> [String] -> [String].

Lastly, head has (specialized) type [String] -> String, and you're trying to postcompose it with getLines. I would guess you're trying to build a function with type String -> [String] -> String, defined by \p ss -> head (getLines p ss). That is, however, not what head . getLines is!

To see this, consider f :: a -> b -> c and g :: c -> d (where I mean to say that the cs are the same type in both signatures, so I'm not really writing proper Haskell signatures here). Since one often likes to think of f as a "function of two variables", one might err and think of g . f as being the function \x y -> g (f x y) (type a -> b -> d). This is not the case: See for example this question and its answers or the concrete example in this answer. Or even better: Look at the type of (.) and work out what g . f must be yourself! (Hint: Every function takes precisely one argument. What is the type of f's one argument?)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top