0
votes

I have defined a couple of type synonyms as follows:

type Potential = Float
type Label = String
type LabelSet = [String]

In addition I have defined the following type and type synonym:

data VariableNode = VariableNode Label Potential LabelSet
type PGM = [VariableNode]

Finally, the following function to construct a graph:

makePGM :: [((Label, Potential), LabelSet)] -> PGM
makePGM (x:xs) = (VariableNode (fst . fst x) (snd . fst x) (snd x)) : makePGM xs
makePGM [] = []

In the above function, a list of tuples is provided where the first element of the tuple is another tuple and the second a list, as per the functions type signature.

I am new to Haskell so am having some difficulty in deciphering the following error messages:

Prelude> :l Graph.hs
[1 of 1] Compiling Graph            ( Graph.hs, interpreted )

Graph.hs:14:33: error:
    • Couldn't match type ‘a0 -> c0’ with ‘[Char]’
      Expected type: Label
        Actual type: a0 -> c0
    • Probable cause: ‘(.)’ is applied to too few arguments
      In the first argument of ‘VariableNode’, namely ‘(fst . fst x)’
      In the first argument of ‘(:)’, namely
        ‘(VariableNode (fst . fst x) (snd . fst x) (snd x))’
      In the expression:
        (VariableNode (fst . fst x) (snd . fst x) (snd x)) : makePGM xs

Graph.hs:14:39: error:
    • Couldn't match expected type ‘a0 -> (c0, b0)’
                  with actual type ‘(Label, Potential)’
    • Possible cause: ‘fst’ is applied to too many arguments
      In the second argument of ‘(.)’, namely ‘fst x’
      In the first argument of ‘VariableNode’, namely ‘(fst . fst x)’
      In the first argument of ‘(:)’, namely
        ‘(VariableNode (fst . fst x) (snd . fst x) (snd x))’

Graph.hs:14:47: error:
    • Couldn't match type ‘a1 -> c1’ with ‘Float’
      Expected type: Potential
        Actual type: a1 -> c1
    • Probable cause: ‘(.)’ is applied to too few arguments
      In the second argument of ‘VariableNode’, namely ‘(snd . fst x)’
      In the first argument of ‘(:)’, namely
        ‘(VariableNode (fst . fst x) (snd . fst x) (snd x))’
      In the expression:
        (VariableNode (fst . fst x) (snd . fst x) (snd x)) : makePGM xs

Graph.hs:14:53: error:
    • Couldn't match expected type ‘a1 -> (a2, c1)’
                  with actual type ‘(Label, Potential)’
    • Possible cause: ‘fst’ is applied to too many arguments
      In the second argument of ‘(.)’, namely ‘fst x’
      In the second argument of ‘VariableNode’, namely ‘(snd . fst x)’
      In the first argument of ‘(:)’, namely
        ‘(VariableNode (fst . fst x) (snd . fst x) (snd x))’
Failed, modules loaded: none.

I have concluded that there are type mismatches but am not clear how so, given the types I have defined and the functions type signature.

2
Maybe try replacing . by $ (both occurrences)? - Ruud Helderman
This seems to work. Thank you. If you don't mind, what was wrong with the function composition I was doing? - Jack H
Haskell grammar says function application always has higher precedence than operators. So your code said the equivalent of \y -> fst (fst x) y if you inline the definition of (.). If you wanted to use function composition there, it could be done with something like (fst . fst) x, but at that point it's getting pretty verbose. - Carl
alternatively, makePGM (x:xs) = (VariableNode (fst . fst $ x) (snd . fst $ x) (snd x)) : makePGM xs. In haskell, function application happens before composition, so you need to be explicit so that it happens in the intended order here. - Erik
Thank you, this has cleared things up for me. - Jack H

2 Answers

2
votes

The problem is that f . g x is f . (g x). Therefore, the types don't match:

fst . fst ((1,2),3)
  == fst . (fst ((1,2),3))
  == fst . (1,2)
  == ???? (.) expects a function, not a value

You either have to use parentheses around fst . fst or $:

-- reminder:
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g x = f (g x)

($) :: (a -> b) -> a -> b
($) f x = f x 

(fst . fst) x
 == fst (fst  x)

fst $ fst x
 == fst (fst x)

You can also combine both, e.g. fst . fst $ x, since the precedence of $ is low.

0
votes

Of course, this is a useful exercise to do, and as a result of making this mistake you've learned to better understand operator precedence and parsing in Haskell. However, once you have more experience you'll realize you could have avoided the problem, and wound up with simpler code as a result, by using pattern matching instead of composing calls to fst and snd:

makePGM :: [((Label, Potential), LabelSet)] -> PGM
makePGM (((label, potential), set):xs) = VariableNode label potential set : makePGM xs
makePGM [] = []

Now the code is shaped like the data it consumes, and you get to give descriptive names to the values you work with.

Another improvement comes from observing that this recursive pattern is very common: you do something to each item in the list, independent of each other, and construct a list of the results. In fact, that is exactly what map does. So you could write a function that deals with only one of these PGM items at a time, and then use map to extend it to a function that works on a list of them:

makePGM :: [((Label, Potential), LabelSet)] -> PGM
makePGM = map go
  where go ((label, potential), set) = VariableNode label potential set