1
votes

Could someone help me to understand this issue.

I have this simple function:

takeAsLong :: [a] -> (a -> Bool) -> [a]
takeAsLong [] _ = []
takeAsLong (x:xs) test | test x    = x : takeAsLong xs test
                       | otherwise = []

But when I try to rewrite it using if-then-else I have an error.

takeAsLong :: [a] -> (a -> Bool) -> [a]
takeAsLong [] _ = []
takeAsLong (x:xs) test = if test x then x : takeAsLong [xs] test else []

The error is in the recursive call of "takeAsLong [xs] test" but I dont understand why it doesnt like it?

Error msg:

Couldn't match type ‘a’ with ‘[a]’ Expected: [a] -> Bool Actual: a -> Bool ‘a’ is a rigid type variable bound by the type signature for: takeAsLong :: forall a. [a] -> (a -> Bool) -> [a]

So why does it expect function test to be [a] -> Bool and why version with guards doesnt have same issue?

1

1 Answers

3
votes

xs is a list of items except the first item. If you use [xs] then you wrapt that list in a singleton list.

You thus should call takeAsLong xs test instead of takeAsLong [xs] test:

takeAsLong :: [a] -> (a -> Bool) -> [a]
takeAsLong [] _ = []
takeAsLong (x:xs) test = if test x then x : takeAsLong xs test else []

Using an if-then-else is not that common. Usually the predicate will also be the first parameter. So a more "Haskell-ish" version is likely:

takeAsLong :: (a -> Bool) -> [a] -> [a]
takeAsLong p = go
    where go [] = []
          go (x:xs)
              | p x = x : go xs
              | otherwise = []

This function already exists and is known as takeWhile :: (a -> Bool) -> [a] -> [a]