Question

I was playing around with .bmp images and ascii art, and came across an issue regarding pattern matching and recursion with a tuple.

The function needs to simply convert the list of words to integers.. While carrying the height and width without change. My attempt:

convertToIntegers :: ([GHC.Word.Word8], Integer, Integer) -> ([Integer], Integer, Integer)
convertToIntegers ([], x, y) =  ([], x, y)
convertToIntegers ((a:as), x, y) = ( toInteger a : convertToIntegers (as, x, y), x, y)

Obviously, this doesn't work and returns a type error:

Couldn't match expected type `[Integer]' with actual type `([Integer], Integer, Integer)'

In the return type of a call of `convertToIntegers' 
In the second argument of `(:)', namely `convertToIntegers (as, x, y)'
In the expression: toInteger a : convertToIntegers (as, x, y)

It seems like it should be simple, and I can accomplish it by splitting it up and using a helper function. In trying to figure this out, I tried the following:

convertToIntegers :: ([GHC.Word.Word8], Int, Int) -> [Integer]
convertToIntegers ([], x, y) =  []
convertToIntegers ((a:as), x, y) = toInteger a : convertToIntegers (as, x, y)

This works fine. So the issue is that I simply don't know how to return a single tuple while manipulating a list inside of it. It's really bugging me, can anyone help?

Was it helpful?

Solution

Why not just use map?

convertToIntegers :: ([GHC.Word.Word8], Integer, Integer) -> ([Integer], Integer, Integer)
convertToIntegers (as, x, y) = (map toInteger as, x, y)

Edit:

Your main issue is that you have an actual bug here:

( toInteger a : convertToIntegers (as, x, y), x, y)
                                    ^  ^  ^

You are returning a tuple as the first argument of another tuple, in the type signature you are claiming to be returning a list of integers. If you didn't want to use map, you could write it like this:

convertToIntegers :: ([GHC.Word.Word8], Integer, Integer) -> ([Integer], Integer, Integer)
convertToIntegers (as, x, y) = (convertWords as, x, y)
    where
        convertWords [] = []
        convertWords (z:zs) = toInteger z : convertWords zs

OTHER TIPS

The standard way to code this use pattern is with let:

convertToIntegers :: ([GHC.Word.Word8], Integer, Integer) -> ([Integer], Integer, Integer)
convertToIntegers ([], x, y) =  ([], x, y)
convertToIntegers ((a:as), x, y) = let (rs, x2, y2) = convertToIntegers (as, x, y)
                                   in  (toInteger a : rs, x2, y2)

Haskell's let is recursive; if you'd use x,y on both sides of the definition inside let, you'd shadow the outer names x,y (block the access to them). This way the x,y on the RHS of the equation refer to the outer arguments x,y, and x2,y2 on the LHS are used to pass along the values returned from the inner function call.

This doesn't mean that this call is made before the values are returned. Quite the contrary, because of Haskell's laziness, first the return value (a triple) is constructed, with holes, (_1, _2, _3) and only when those holes will be accessed this will trigger the further evaluation according to the definition, with _1 = toInteger a : rs, _2 = x2, _3 = y2. So if that access is head _1, no function call will be triggered yet.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top