1

I'm learning Haskell and working on a Hamming distance exercise.

module Hamming (distance) where

distance :: String -> String -> Maybe Int
distance (x:xs) (y:ys)
  | lengthX /= lengthY  = Nothing
  | lengthX == 0        = Just 0
  | otherwise           = Just (headDistance + tailDistance)
  where
    lengthX = length (x:xs)
    lengthY = length (y:ys)
    headDistance = if x /= y then 1 else 0
    (Just tailDistance) = distance xs ys

When I run it, distance "" "" gives the error:

haskell/hamming » stack test
hamming> test (suite: test)


distance
  empty strands FAILED [1]

Failures:

  src/Hamming.hs:4:1:
  1) distance empty strands
       uncaught exception: PatternMatchFail
       src/Hamming.hs:(4,1)-(12,40): Non-exhaustive patterns in function distance

  To rerun use: --match "/distance/empty strands/"

Randomized with seed 892503112

Finished in 0.0003 seconds
1 example, 1 failure

hamming> Test suite test failed
Test suite failure for package hamming-2.3.0.10
    test:  exited with: ExitFailure 1
Logs printed to console

I used guards instead of pattern matching for the main stuff, so it looks like it's that last where statement is the only place where pattern matching occurs, and must be what is causing the error. I see that it doesn't match against the Nothing output.

However, I don't see how it could ever produce Nothing, since things get evaluated lazily, I thought. In the case where the original strings are of different lengths, Nothing will be returned immediately and distance will never be called recursively, right?

Adam Zerner
  • 17,797
  • 15
  • 90
  • 156
  • 2
    the empty string does not match `(x:xs)` or `(y:ys)` - and that's your problem here – Robin Zigmond Mar 01 '21 at 00:27
  • @RobinZigmond Gotcha that was it, thanks! I overlooked that and was focused on that `where` statement but it makes sense in retrospect. If you'd like to write up your comment as an answer I can accept it. – Adam Zerner Mar 01 '21 at 00:33
  • 1
    If you turn on `-Wall`, GHC will tell you when your patterns are non-exhaustive — very useful in cases like this! – bradrn Mar 01 '21 at 00:33

1 Answers1

2

This error isn't anything to do with your guards - it's much more basic than that.

You have:

distance :: String -> String -> Maybe Int
distance (x:xs) (y:ys)
- -  more code

but no other cases. So you do indeed have non-exhaustive patterns, because the only case you cover is when the first argument matches (x:xs), and the second (y:ys) so it will crash as soon as either argument is empty - let alone both, as you are calling it here.

Robin Zigmond
  • 17,805
  • 2
  • 23
  • 34