Error Could not match type a1 with type int, Error while solving problem count the number of even integers in an array

I am trying to solve a problem from book purescript by example
Write a recursive function countEven which counts the number of even integers in an array
Here’s my code below

isEvenInt :: Int -> Int
isEvenInt a = 
if a `mod` 2 == 0
 then 1
 else 0

 countEven :: forall a. Array a -> Int
 countEven ar = 
    if null ar 
     then 0
     else  isEvenInt (fromMaybe 0 (head ar))  + (countEven $ fromMaybe [] $ tail ar )

It’s giving some errors and I am not able to figure out what to do

Error found:
in module Test.MySolutions
at test/MySolutions.purs:26:40 - 26:47 (line 26, column 40 - line 26, column 47)

Could not match type
with type
   while trying to match type Maybe t0
     with type Maybe Int
   while checking that expression head ar
     has type Maybe Int
  in binding group countEven

  where a1 is a rigid type variable
    bound at (line 0, column 0 - line 0, column 0)
  t0 is an unknown type

if someone could tell what’s wrong with my code and what is error is all about?

You annotate countEven to take arrays of any type

but isEvenInt only takes Int. So when you use head on an Array a you gets a’s back which won’t fit isEvenInt.

So it complains that it cant match a1(the “a” hints at the origin of the type variable) to Int.

1 Like

so are you saying that when a call is made to isEvenInt an array is passed to it rather than an int?

No. When you write forall a. Array a -> Int, you are saying, “This is a function that can take an array that contains values of ANY type and it returns a value of type Int.” In other words, passing in ["a", "b", "c"] would be a valid argument and passing in [0, 1, 2] would be a valid argument.

However, in your implementation, you write fromMaybe 0 (head ar).

When we look at the type signature for head, we see this: head :: forall a. Array a -> Maybe a. As in, “if you give me an array, I will give you the first element of that array if it exists. The resulting value’s type (i.e. a) is the same as the Array’s element type (i.e. a).”

Then, you pass the result of that into fromMaybe. Looking at the type signature and definition, we see this:

fromMaybe :: forall a. a -> Maybe a -> a`
fromMaybe defaultValue maybeValue = case maybeValue of
  Nothing -> defaultValue
  Just value -> value

This is where the compiler finds an error. If I were to make the “polymorphic” types “monomorphic”, you’re writing this:

fromMaybe :: forall a. Int -> Maybe a -> ???
fromMaybe 0 maybeValue = case maybeValue of
  Nothing -> 0
  Just a -> a

As a result, you are saying "this function will return two different types: either a value of type Int or a value of type a. That is not possible since both branches must return a value of the same type. So the compiler emits a “Type Unification error” because Int is not the same as a. In some cases it could be, such as if I pass in [0, 1, 2] as my ar argument. But it won’t be if I pass in ["a", "b", "c"] as my argument. When you use forall a., you are saying it will work for all/any types, but the compiler found at least one situation where it does not.