Search code examples
debugginghaskelltypestypecheckingpointfree

Why doesn't this typecheck?


compress xs@(_:_:_) = (ifte <$> ((==) <$> head <*> head.tail) <$> ((compress.).(:) <$> head <*> tail.tail) <*> ((:) <$> head <*> compress.tail) ) xs

Results in a type error, but I can't see why. It should be equivalent to

compress xs@(_:_:_) = (ifte (((==) <$> head <*> head.tail) xs) (((compress.).(:) <$> head <*> tail.tail) xs) (((:) <$> head <*> compress.tail) xs))

, which doesn't.

note: ifte = (\ x y z -> if x then y else z) , <$> and <*> are from Control.Applicative .

EDIT: The error is:

Couldn't match expected type `[a]' with actual type `[a] -> [a]'
    In the expression:
        (ifte <$> ((==) <$> head <*> head . tail)
     <$>
       ((compress .) . (:) <$> head <*> tail . tail)
   <*>
     ((:) <$> head <*> compress . tail))
      $ xs
    In an equation for `compress':
        compress xs@(_ : _ : _)
          = (ifte <$> ((==) <$> head <*> head . tail)
         <$>
           ((compress .) . (:) <$> head <*> tail . tail)
       <*>
         ((:) <$> head <*> compress . tail))
          $ xs

I encountered this problem trying to write a pointfree solution to problem 8 of the Ninety-Nine Haskell Problems. I was trying to do it by modifying the pointful solution I had written, which was

compress::Eq a => [a]->[a]
compress [] = []
compress (x:[]) = (x:[])
compress (x:y:xs) = ifte ((==) x y) (compress (x:xs)) (x:(compress (y:xs)))

Solution

  • First, indent. Second, consider using some variables.

    Even with more sensible formatting, you can see that it's

    compress =
      ifte <$> ((==) <$> head <*> head.tail)
           <$> ((compress.).(:) <$> head <*> tail.tail)
           <*> ((:) <$> head <*> compress.tail)
    

    when it should be

    compress =
      ifte <$> ((==) <$> head <*> head.tail)
           <*> ((compress.).(:) <$> head <*> tail.tail)
           <*> ((:) <$> head <*> compress.tail)
    

    Third, even if you must be inscrutable, how about

    compress (x:r@(y:_)) = ifte (x==y) id (x:) $ compress r
    

    or, point free

    compress = map fst . filter (uncurry (/=)) . (zip <$> id <*> tail)