haskellfunctional-programmingnon-exhaustive-patterns

How to implement a function to change array cells avoiding non-exhaustive pattern errors in haskell?


My goal is to write a function called walk, which switches the value of a labyrinth cell with its neighbour. For example calling walk 0 labyrinthA should move the T one cell to the left.Here i tried to change a value of a labyrinth cell in ghci.

showLabyrinth labyrinth =
   putStrLn $ unlines $ [[labyrinth j i | i <- [1..dimH]] | j <- [1..dimV]]
 where
   dimH = length . takeWhile (/='O') $ [labyrinth 1 i | i <- [1..]]
   dimV = length . takeWhile (/='O') $ [labyrinth i 1 | i <- [1..]]

labyrinthA 9 _  = 'O'
labyrinthA _ 16 = 'X'
labyrinthA _ 17 = 'O'
labyrinthA 1 _  = 'X'
labyrinthA 2 1  = 'X'
labyrinthA 2 8  = 'X'
labyrinthA 2 12 = 'X'
labyrinthA 2 _  = ' '
labyrinthA 3 1  = 'X'
labyrinthA 3 3  = 'X'
labyrinthA 3 5  = 'X'
labyrinthA 3 6  = 'X'
labyrinthA 3 8  = 'X'
labyrinthA 3 9  = 'X'
labyrinthA 3 10 = 'X'
labyrinthA 3 12 = 'X'
labyrinthA 3 14 = 'M'
labyrinthA 3 _  = ' '
labyrinthA 4 1  = 'X'
labyrinthA 4 2  = 'X'
labyrinthA 4 3  = 'X'
labyrinthA 4 5  = 'X'
labyrinthA 4 6  = 'X'
labyrinthA 4 12 = 'X'
labyrinthA 4 _  = ' '
labyrinthA 5 1  = 'X'
labyrinthA 5 5  = 'X'
labyrinthA 5 8  = 'X'
labyrinthA 5 9  = 'X'
labyrinthA 5 10 = 'X'
labyrinthA 5 12 = 'X'
labyrinthA 5 13 = 'X'
labyrinthA 5 15 = 'X'
labyrinthA 5 _  = ' '
labyrinthA 6 1  = 'X'
labyrinthA 6 3  = 'X'
labyrinthA 6 4  = 'X'
labyrinthA 6 5  = 'X'
labyrinthA 6 7  = 'X'
labyrinthA 6 8  = 'X'
labyrinthA 6 10 = 'X'
labyrinthA 6 _  = ' '
labyrinthA 7 1  = 'X'
labyrinthA 7 6  = 'T'
labyrinthA 7 9  = 'X'
labyrinthA 7 10 = 'X'
labyrinthA 7 11 = 'X'
labyrinthA 7 12 = 'X'
labyrinthA 7 13 = 'X'
labyrinthA 7 14 = 'X'
labyrinthA 7 15 = 'X'
labyrinthA 7 _  = ' '
labyrinthA 8 6  = 'E'
labyrinthA 8 _  = 'X'

Now i tried to let the 'T' walk to the left with walk 0 labyrinthA

walk direction labyrinth | direction == 0 = let labyrinth (yKoord 'T') 
(xKoord 'T') = ' ' && let labyrinth (yKoord) ((xKoord+1)) = 'T'
                          | direction == 1 = undefined
                          | direction == 2 = undefined
                          | direction == 3 = undefined 

Where as yKoord and xKoord are integers.
enter image description here


Solution

  • The error you’re getting is because the patterns don’t cover every case. An exhaustive pattern is one that exhausts all the possibilities, that is, returns a result for every potential input value.

    Here, what would happen if you called, for example, labyrinthA 10 1? Or labyrinthA 0 0? If your compiler is not warning you about this bug at compile time, you might want to compile with the -Wall flag to turn warnings on.

    If the program logic makes it impossible to call the function with any other values (in which case it should probably be a local function that can only be called from one place), there is a quick fix. Add a line like this to the bottom of your function:

    labyrinthA x y = error $ "Domain error: labyrinthA " ++ show x ++
                             " " ++ show y
    

    This adds a catch-all clause matching any patterns that haven’t been covered already, satisfying the compiler. If the function does get called with “impossible” values, the logic error will now crash the program with a more-informative debug message.

    In your case, some other part of your program that you haven’t shown us calls your function with values outside its domain, which is giving you a runtime crash. Unfortunately, we cannot help you without being able to see the code, but tracing what sequence of calls led to the crash would be a good first step.

    In this specific instance, you probably don’t want to update the map of your labyrinth every time the player takes a step. That’s not a very efficient or elegant operation in a functional language. You might, instead, store the immutable map and then update the position of your player, to draw on top of it. The (X,Y) coordinates could be another parameter or part of program-state data that you pass around.

    This doesn’t answer your question about modifying a function after it’s been defined. I think that’s an XY problem, but here’s a literal answer to that part of the question. You cannot do this with the String type. There are ways to get mutable state in Haskell, such as a mutable Data.Vector.MVector with the ST type, but that’s not your immediate problem.