Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Haskell, how do you use 'head' without creating a separate function?

Tags:

haskell

head

I'm very new to using Haskell and I'm unsure about how 'head' works. From my understanding it returns the first element in a list. I've been trying to use it, but I keep getting errors. I added a workaround by creating a separate function that finds the head, however that seems like it should be unnecessary.

I don't understand why calling findHead here:

single x = length (snd(x)) == 1

toList board
    | board == [] = []
    | otherwise = filter single board

findHead board = head (toList board)

is not equivalent to calling toList here:

single x = length (snd(x)) == 1

toList board
    | board == [] = []
    | otherwise =  head (filter single board)

It seems to me that the two should be the same, but only the first one runs. Why aren't they interpreted to be the same? Could you explain this to me? In the code above, 'board' is meant to be a list of tuples which are each the form (x, [a,b,...]).

I've used 'head' in a few simpler things like:

union xs ys 
  | xs == [] = ys
  | ys == [] = xs
  | otherwise = union (tail xs)  (add (head xs) ys)

which seems to work as I would expect it would.

like image 878
bananaruth Avatar asked Dec 20 '22 05:12

bananaruth


2 Answers

head is partial. In particular, head [] doesn't return normally (throws an exception). This can be difficult to handle in Haskell which is why people often suggest that you avoid partial functions.

So how do we do that? We have to reflect failure in the type.

safeHead :: [a] -> Maybe a
safeHead []     = Nothing
safeHead (a:as) = Just a

A similar function can be made for tail

safeTail :: [a] -> Maybe [a]
safeTail []     = Nothing
safeTail (a:as) = Just as
like image 109
J. Abrahamson Avatar answered May 14 '23 19:05

J. Abrahamson


I think you need to take a step back and understand how pattern matching, recursion and single-linked lists work. As various people have mentioned, the code you're writing is not going to work very well.

As an illustration here's how I would write the various functions in your question:

single :: (a, [b]) -> Bool    
single (_, [_]) = True
single _ = False

toList :: [(a, [b])] -> [(a, [b])]
toList board = filter single board

findHead :: [(a, [b])] -> Maybe (a, [b])
findHead [] = Nothing
findHead board = head (toList board)

-- This one actually does the same thing as the built-in `++` operator,
-- but for the sake of illustration, I'll implement it.
union :: [a] -> [a] -> [a]
union [] ys = ys
union (x:xs) ys = x : union xs ys
like image 38
Luis Casillas Avatar answered May 14 '23 20:05

Luis Casillas