Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell Prelude.head error, empty list

My code is throwing an empty list error. When I run:

makeAgent :: Agent -> [Agent] -> Agent
makeAgent (Agent func n _) agents = (Agent func (n++(show $ length $ sameNames n agents)) empty) --appends number to name to differentiate agents
    where sameNames n agents = filter (findName n) agents
          findName n1 (Agent _ n2 _) = (slice 0 3 n1) == (slice 0 3 n2) --ignore the suffix
          empty = head $ getEmpty (positions agents) (fst $ getGrid agents) --getGrid returns a tuple, but currently assume to be a square

baseline :: [Interaction] -> Float
baseline int = (fromIntegral total)/len
  where total = sum sums
        sums = map snd (showSums int)
        agents = nub  $ map (\(Interaction a1 a2 _ ) ->  a2) int
        len  = fromIntegral $ length agents

reproduce :: Float -> [Interaction] -> [Agent]  --so baseline isn't recalulated every time
reproduce _ [] = []
reproduce base interaction = winners ++ [newAgent] ++ reproduce base (tail interaction)
    where agents = nub $ concat $ map (\(Interaction a1 a2 _ ) -> a1:a2:[]) interaction
          winners = [a | a <- agents, (sumAgent interaction a) >= (round base)]
          newAgent = makeAgent (head winners) winners


main = do
    output "Length" (fromIntegral $ length int)
    output "Baseline" base
    output "Agents" agents
    output "Sums" (showSums int)
    output "winners" winners
    output "NeAgent" (makeAgent (head winners)winners)
    output "New Agents" (reproduce base int)

    where agents = generate 4
          int = playRound agents 20
          base = baseline int
          winners = [a | a <- agents, (sumAgent int a) >= (round base)]

What this reproduce, the main function, should be doing is generating a new agent based on whether their parents "fitness" is over a certain level, then run the same function with all but that agent in the list of agents.

It outputs:

Length: 16
Baseline: 280.0
Agents: [c_pavlov(-1,-1),c_titForTat(-1,0),c_sucker(-1,1),b_grim(0,-1)]
Sums: [("c_pavlov",280),("c_titForTat",280),("c_sucker",280),("b_grim",280)]
winners: [c_pavlov(-1,-1),c_titForTat(-1,0),c_sucker(-1,1),b_grim(0,-1)]
NeAgent: c_pavlov1(0,0)
prisoners: Prelude.head: empty list

When I call reproduce, it throws the prelude.head empty list error, and the winners,agent, and int lists are all non-empty, so it may be an edge case error on the last iteration of the recursion. Why is this happening?

like image 983
MichaelFine Avatar asked Jul 24 '26 21:07

MichaelFine


1 Answers

The solution is relatively simple: Simply do not use head and tail in Haskell programs. (While there are a few situation where it is reasonable to do so, it's best to assume in the beginning that there aren't.)

Instead, use pattern matching. Usually, you will always want to properly pattern match on both the empty list and the non-empty list, like this:

fun :: [Something] -> ...
fun []       = ...
fun (x : xs) = ... -- x is head, xs is tail

This way, you are forced to deal with the error, and you know that referring to x and xs cannot fail anymore, because the list has already been determined to be non-empty.

If you have lots of lists in your program that should never be empty, then you have to manually keep track of these conditions (and should at least document them). But even then, it's better to pattern match in a let-binding like this

(winner : _) = ...

and then refer to winner as the head of that list later, because you will get an error message involving the line number where the pattern match failed.

like image 68
kosmikus Avatar answered Jul 26 '26 14:07

kosmikus