head :: [a] -> a
-- versus
saferHead :: [a] -> Maybe a
-- versus
safeHead :: NonEmpty a -> a
The first function is partial because we'll get an exception if we give it the empty list.The second function is total but annoying to use because we're telling all the code that uses the result to check for two cases (it's an error not to).
The last one forces the responsibility on the caller to provide a non-empty input.
That was probably one of the more frustrating aspects about learning to program Haskell as someone who has been programming for more than fifteen years when I started. It revealed to me in stunning detail all of the edge cases that almost every other language I've used actively hides from me.
It doesn't absolve you of having to think about edge cases: even Haskell throws run-time exceptions. However it does give you tools to think about many of those edge cases up-front and in a direct way.
Update Added a trivial example to demonstrate "partial," etc.