Home > Software design >  why my own 'index' function fails to run?
why my own 'index' function fails to run?

Time:10-02

I am studying haskell language , doing some practices. Now I am attempting to implement my own 'index' function , meaning it can give the index of the given element within a list, but now my version fails to run , as the following

index' n (s:x) 
    | s/=n  =  1 index' x
    | otherwise  =  0
main = print(index' 3 [1,2,3,5,6,7,9])

However , if simplifying the argument into one and set the target number in the guard , as the following , it run.

index' (s:x) 
    | s/=3  =  1 index' x
    | otherwise  =  0
main = print(index' [1,2,3,5,6,7,9])

I think there may be a elemental mistake in my understanding , and I need a help , please !

CodePudding user response:

If index' takes two arguments, then it always takes two arguments. This includes the time when you call it recursively. Rather than

1 index' x

Consider

1 index' n x

CodePudding user response:

To fix the question, as @chi has said, you should always start with a type declaration. In principle, Haskell can figure out the types, but if you make a mistake then Haskell will get it wrong too - very confusing. Also, you should be planning your function before you code. Without a type declaration, the nonsensical GHC error message is this:

Test.hs:2:1: error:
    * Occurs check: cannot construct the infinite type: a ~ [a]
      Expected type: [a] -> p
      Actual type: a -> [a] -> p
    * Relevant bindings include
       index' :: [a] -> p (bound at Test.hs:2:1)
  |
2 | index' n (s:x) 
  |

The type signature could be index' :: Int -> [Int] -> Int. This says that I give it an Int and an array of Int, and it returns an Int. That would do for your example, but is unnecessarily restrictive. The input types can be anything provided that we can do the equality test. Hence:

index' :: (Eq a) => a -> [a] -> Int
index' n (s:x) 
    | s/=n  =  1 index' x
    | otherwise  =  0
main = print(index' 3 [1,2,3,5,6,7,9])

I am using Haskell Language Server (HLS) in Visual Studio Code. The error it gives me is that the pattern matches are not exclusive. GHC gives me the following (much improved) error, now that my type declaration is in:

Test.hs:3:16: error:
    * Couldn't match expected type `Int'
                  with actual type `[[a]] -> Int'
    * Probable cause: `( )' is applied to too few arguments
      In the expression: 1   index' x
      In an equation for index':
          index' n (s : x)
             | s /= n = 1   index' x
             | otherwise = 0
    * Relevant bindings include
        x :: [a] (bound at Test.hs:2:13)
        s :: a (bound at Test.hs:2:11)
        n :: a (bound at Test.hs:2:8)
        index' :: a -> [a] -> Int (bound at Test.hs:2:1)
  |
3 |     | s/=n  =  1 index' x
  |                ^^^^^^^^^^

So we have two problems.

Firstly, dealing with pattern matches. HLS says that I'm not handling _ [], that is to say irrespective of the value of n, the case where a zero length list is provided. So, we add that explicitly:

index' :: (Eq a) => a -> [a] -> Int
index' _ [] = 0
index' n (s:x) 
    | s/=n  =  1 index' x
    | otherwise  =  0
main = print(index' 3 [1,2,3,5,6,7,9])

Now, HLS is highlighting the same code as GHC, and with the same error message. We haven't provided enough arguments. Let's fix that:

index' :: (Eq a) => a -> [a] -> Int
index' _ [] = 0
index' n (s:x) 
     | s/=n  =  1 index' n x
     | otherwise  =  0

main :: IO ()
main = print(index' 3 [1,2,3,5,6,7,9])

That works. Although there's a problem of what happens if the value isn't found. We'd should consider using Maybe - a topic for another day.

  • Related