Why can't I write the function like this?

  • A+
Category:Languages

I am studying for an "Introduction to Functional Programming" exam. This is one of the problems I am stuck on:

"The following datatypes are used to represent a hand of cards:

data Suit = Hearts | Clubs | Diamonds | Spades                              deriving Eq  data Rank = Numeric Int | Jack | Queen | King | Ace                          deriving Eq  data Card = NormalCard Rank Suit | Joker      deriving Eq 

Define a function

countAces:: [Card] -> Int countAces = undefined 

where countAces returns the number of cards in the given hand which are either aces or jokers. So for example, if there are three aces and two jokers in the hand, the answer will be five."

So I thought I would write it like this:

countAces:: [Card] -> Int countAces []                               = 0 countAces (c:cs) | c == (NormalCard Ace _) = 1 + countAces (cs)                  | c == Joker              = 1 + countAces (cs)                  | otherwise               = countAces (cs) 

But this won't compile, and I have understood that I can't write c == (NormalCard Ace _). But if I change the function to:

countAces:: [Card] -> Int countAces []                         = 0 countAces (c : cs) = countCard c + countAces cs   where countCard Joker              = 1         countCard (NormalCard Ace _) = 1         countCard _                  = 0 

Then it works! So my question is, why doesn't the first version work?

This is the error:

    * Found hole: _ :: Suit     * In the second argument of `NormalCard', namely `_'       In the second argument of `(==)', namely `(NormalCard Ace _)'       In the expression: c == (NormalCard Ace _)     * Relevant bindings include         cs :: [Card] (bound at exam.hs:96:14)         c :: Card (bound at exam.hs:96:12)         countAces :: [Card] -> Int (bound at exam.hs:95:1)       Valid substitutions include         Hearts :: Suit (defined at exam.hs:87:13)         Clubs :: Suit (defined at exam.hs:87:22)         Diamonds :: Suit (defined at exam.hs:87:30)         Spades :: Suit (defined at exam.hs:87:41)         undefined :: forall (a :: TYPE r).                      GHC.Stack.Types.HasCallStack =>                      a           (imported from `Prelude' at exam.hs:1:1            (and originally defined in `GHC.Err')) 

Thanks a ton to anyone who took the time to read this.

 


This question is more about your mental model of what things mean, not about Haskell itself (if you ask about Haskell itself, then the answer is: "because that's how the language is works").

So I will try to just appeal to your imagination:

In the first case you have an expression, which will evaluate to True or False - both are valid outcomes. You are performing a comparison, using an existing function: (==). This function takes two values - and you need to provide them fully, without holes - for exactly the same reason, why you can't write (2 + _) * 10 and expect it to evaluate to a number.

In the second case, you use a language construct =. This construct is not a function that returns a value. It is used to build a definition. When you write a = 2, you are not writing an expression which can be true or false. You are defining a in terms of 2. It will either work and be forever true - or will not compile. In this context - you can use holes. When you write a _ = 2, you really say: No matter what you apply to a you will get 2.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: