2

I am reading Real World Haskell, trying to solve Ch3, Q10 using ghc online.

So far I have the following code:

data Direction point = MyLeft point | MyRight point | Straight deriving (Show)

getDirectionFromTriple :: Direction p -> Direction p -> Direction p -> Direction p
getDirectionFromTriple p1 p2 p3 
  | (length . filter (== MyLeft) [p1, p2, p3]) > 1 = MyLeft p3
  | (length . filter (== MyRight) [p1, p2, p3]) > 1 = MyRight p3
  | otherwise = Straight

I receive the following error when trying to compile this code (only parts posted, the same error pops up several times) :

[1 of 1] Compiling Main             ( jdoodle.hs, jdoodle.o )


jdoodle.hs:17:15: error:
    * Couldn't match expected type `a0 -> t0 a1'
                  with actual type `[point0 -> Direction point0]'
    * Possible cause: `filter' is applied to too many arguments
      In the second argument of `(.)', namely
        `filter (== MyLeft) [p1, p2, p3]'
      In the first argument of `(>)', namely
        `(length . filter (== MyLeft) [p1, p2, p3])'
      In the expression: (length . filter (== MyLeft) [p1, p2, p3]) > 2
   |
17 |   | (length . filter (== MyLeft) [p1, p2, p3]) > 2 = MyLeft p3
   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

jdoodle.hs:17:35: error:
    * Couldn't match expected type `point0 -> Direction point0'
                  with actual type `Direction p'
    * In the expression: p1
      In the second argument of `filter', namely `[p1, p2, p3]'
      In the second argument of `(.)', namely
        `filter (== MyLeft) [p1, p2, p3]'
    * Relevant bindings include
        p3 :: Direction p (bound at jdoodle.hs:16:30)
        p2 :: Direction p (bound at jdoodle.hs:16:27)
        p1 :: Direction p (bound at jdoodle.hs:16:24)
        getDirectionFromTriple :: Direction p
                                  -> Direction p -> Direction p -> Direction p
          (bound at jdoodle.hs:16:1)
   |
17 |   | (length . filter (== MyLeft) [p1, p2, p3]) > 2 = MyLeft p3
   |                                   ^^

jdoodle.hs:17:39: error:
    * Couldn't match expected type `point0 -> Direction point0'
                  with actual type `Direction p'
    * In the expression: p2
      In the second argument of `filter', namely `[p1, p2, p3]'
      In the second argument of `(.)', namely
        `filter (== MyLeft) [p1, p2, p3]'
    * Relevant bindings include
        p3 :: Direction p (bound at jdoodle.hs:16:30)
        p2 :: Direction p (bound at jdoodle.hs:16:27)
        p1 :: Direction p (bound at jdoodle.hs:16:24)
        getDirectionFromTriple :: Direction p
                                  -> Direction p -> Direction p -> Direction p
          (bound at jdoodle.hs:16:1)
   |
17 |   | (length . filter (== MyLeft) [p1, p2, p3]) > 2 = MyLeft p3
   |                                       ^^

I would appreciate either suggestions how to fix my code or suggestions for more succint solutions determining the dominant direction from a triple of points.

mushishi
  • 141
  • 1
  • 10
  • 1
    Among other errors: since `p3 :: Direction p`, `MyLeft p3 :: Direction (Direction p)`, which is the wrong type. – chi Oct 01 '19 at 17:53
  • Aside: `length xs > 1` traverses the whole spine of `xs` because `Int` is strict; it doesn’t matter here, but if `length xs` is expected to be large (or infinite) then you can make this more efficient using for example `not (small xs)` where `small = null . drop 1` – Jon Purdy Oct 02 '19 at 09:50

2 Answers2

2

Same as the recent question Why does odd.fst not work with the filter function?. When you write

length . filter f xs

this is parsed as

length . (filter f xs)

This clearly isn't right: filter f xs is a list, not a function, so it makes no sense to compose it with length. Instead, you could write

(length . filter f) xs

although a more popular way to spell that would be

length . filter f $ xs
amalloy
  • 89,153
  • 8
  • 140
  • 205
  • None of the following avoid the above error: (length . filter (== MyLeft) $ [p1, p2, p3]) (length . filter (== MyLeft) [p1, p2, p3]) ((length . filter (== MyLeft)) [p1, p2, p3]) (length $ filter (== MyLeft) [p1, p2, p3]) – mushishi Oct 02 '19 at 09:58
  • Those suggestions fix your first type error, but not the second (that `(== MyLeft)` is itself not a very good function). – amalloy Oct 02 '19 at 15:27
2

You can create functions that tells you if it is right or left instead of using ==

import Data.List 

data Direction point = MyLeft point | MyRight point | Straight deriving (Show)

getDirectionFromTriple :: Direction p -> Direction p -> Direction p -> Direction p
getDirectionFromTriple p1 p2 p3 
  | isDirection isLeft  [p1, p2, p3]  = MyLeft (getValue p3)
  | isDirection isRight [p1, p2, p3]  = MyRight (getValue p3)
  | otherwise = Straight

isDirection :: (Direction p -> Bool) -> [Direction p] -> Bool
isDirection f ps = (length . filter f) ps > 1

getValue (MyLeft a) = a
getValue (MyRight a) = a
getValue Straight  = error "No value in Straight"

isLeft  (MyLeft _)  = True
isLeft   _          = False
isRight (MyRight _) = True
isRight  _          = False

main = do
  putStrLn $ show $ getDirectionFromTriple (MyRight 2) Straight (MyLeft 1)
  putStrLn $ show $ getDirectionFromTriple (MyRight 2) (MyRight 3) (MyLeft 1)
  putStrLn $ show $ getDirectionFromTriple (MyLeft 1) (MyLeft 1) (MyRight 2) 
developer_hatch
  • 15,898
  • 3
  • 42
  • 75