Congrats on your first project!

In general, there are three approaches I use when dealing with Maybe:
- Use helper functions such as
withDefault and map to avoid explicit casing.
-
Pushing uncertainty to the edges of your code by separating code that checks for presence from code that calculates values.
-
Modeling your code using other structures such that the need for
Maybe is eliminated entirely.
In your case, it looks like most of the Maybes are introduced by calling List.head. I’m thinking a List may not be the best structure to model.
Some alternatives might be:
Library
List.NonEmpty as suggested by @pdamoc above
Custom Type
A custom type that guarantees you a head:
type alias Snake =
{ head : Coordinate
, tail : List Coordinate
}
-- OR
type Snake = Snake Coordinate (List Coordinate)
Note that these are basically custom versions of List.NonEmpty
Math
You could take a completely different approach and never store every segment of the snake. Instead you’d store the position of the head, the length of the snake, and the coordinates of any turns.
type alias Snake =
{ head : Coordinate
, length : Int
, turns : List Coordinate
}
Then you’d use math to figure out if the snake intersects itself. This could make some things easier like lengthening or shortening the snake.