1

To start with I have created a Type StudentMark which is a tuple taking firstly a String and secondly an Int.

type StudentMark = (String, Int)

This is my capMarks function:

capMarks :: [StudentMark] -> [StudentMark]
capMarks [cMarks] = [(st, mk) | (st, mk) <- [capMark cMarks]]

And here is my capMark function:

capMark :: StudentMark -> StudentMark
capMark (st, mk)
    |   mk > 39   =   (st, 40)
    |   mk < 40   =   (st, mk)

It is supposed to return:

[("Jo", 37), ("Sam", 40)]

from:

capMarks [("Jo", 37), ("Sam", 76)]

But will only return the correct and expected response when I input just 1 parameter into the function, for example:

capMarks [("Jake", 50)]

Or

capMarks [("Jake"), 30]

But using two (or more) as it's supposed to will just tell me there is a Non-exhaustive pattern in the capMarks function.

GarethAS
  • 331
  • 2
  • 12
  • If any of my terminology is off please bear with me as I'm new to both Haskell and stackoverflow – GarethAS Jan 22 '16 at 18:08
  • Let's back up a step. What are you trying to accomplish with these functions? What is the purpose of this code? What problem are you actually trying to solve with this code? – Code-Apprentice Jan 22 '16 at 18:20
  • It is supposed to return the tuples wherein the Integers over 40 will be returned as 40 as per the capMark function. i.e. `capMark ("Steve", 100)` would return `("Steve", 40)` so what the capMarks function is supposed to do is use the capMark function to do this for a list of tuples using a list comprehension. – GarethAS Jan 22 '16 at 18:31

2 Answers2

4

Let's analyze your capMarks function:

capMarks :: [StudentMark] -> [StudentMark]
capMarks [cMarks] = [(st, mk) | (st, mk) <- [capMark cMarks]]

First of all capMarks [cMarks] = ... is a pattern matching. This matches a list that contains a single element. I assume that you want to do something with an entire list, so change this to capMarks cMarks = ...

Next ... [(st, mk) | (st, mk) <- [capMark cMarks]] will apply the capMark function to the only element in your original pattern matching scheme and then put the result as the only element of a list. It appears that you want to apply capMark to each element of a list. So if we follow the previous suggestion, you need to do something like ... [capMark mark | mark <- cMarks]. This does exactly as stated earlier: apply capMark to each element of the cMarks list.

Final version:

capMarks :: [StudentMark] -> [StudentMark]
capMarks cMarks = [capMark mark | mark <- cMarks]

Alternatively, you can also use pattern matching and explicit recursion:

capMarks [] = []  
capMarks (x:xs) = capMark x : capMarks xs

The first line says that capMarks applied to an empty list is an empty list. The second line says that capMarks applies to a list with at least one element will apply capMark to the first element and then recursively apply capMarks to the rest of the list.

This is such a common pattern in Haskell that there is a function called map that generalizes it. Using map is incredibly simple:

capMarks cMarks = map capMark cMarks

map has type (a -> b) -> [a] -> [b] which means it takes a function and a list and returns a list. (The a and b just tell the compiler which types have to be the same.) map then applies the function to each element in the input list.

Eventually you will learn about partial function application and point-free style. With these two concepts, the version using map can be simplified slightly:

capMarks = map capMark

Don't worry too much about this yet. I'm just adding it here for completeness.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
  • Code-Apprentice, I see the issue I had now! I thought that my capMark function would have to be on the pattern matching side of the comprehension. This makes a lot more sense now, thank you. – GarethAS Jan 22 '16 at 18:36
  • @GarethAllen-Stringer I edited my question with a bit more explanation, including some alternative implementations. I hope this helps. Good luck with learning Haskell. – Code-Apprentice Jan 22 '16 at 18:38
  • Ah I see how map works from your example. We've not yet been taught that but I shall bear it in mind for the future as it looks like it will get a lot of use. Your completeness is much appreciated. I have seen `(x:xs)` but not understood what it meant or how it worked. – GarethAS Jan 22 '16 at 18:44
  • @GarethAllen-Stringer I'm sure you will get to this in your class eventually. `(x:xs)` as a function parameter is a pattern which matches a list with at least one element. `x` is the first element of the list and `xs` is the rest of the list, possibly even an empty list. – Code-Apprentice Jan 22 '16 at 18:47
1

You should check how pattern matching works in Haskell.

capMarks [x] will only match a list with one element. What you probably want is something like capMarks myList = [ ... | ... <- f myList] or define the rests of the cases in a recursive manner.

For example

capMarks [] = []
capMarks x:xs = capMark x : capMarks xs

This simplified "version" works in hugs

capMarks :: [Integer] -> [Integer]
capMarks xs = [(*) 2 x | x <- xs]
Ale
  • 1,315
  • 9
  • 19
  • My Haskell is rusty :-/ – Ale Jan 22 '16 at 18:19
  • I have my head around most of it, but now that it's calling on a previous function there's a lot of issues regarding mis-matching types. I follow your simplified version though. – GarethAS Jan 22 '16 at 18:21
  • The issue seems to me to be that the variable cMarks is a list of tuples (StudentMark) but the function capMark will only take a single StudentMark, not a list of them. – GarethAS Jan 22 '16 at 18:24
  • Well in haskell you have the types to think of the stuff. `capMark` can only by applied to a student, so if you are going to use comprehension to extract the elements of the list, you should apply `capMark` to the elements from the list. Think of the simplified version I just showed you, if you think `(*) 2` as a `capMark`... how would you write capMarks. – Ale Jan 22 '16 at 18:27
  • Ale, thanks for your explanation on list comprehensions. Much appreciated. – GarethAS Jan 22 '16 at 18:38
  • Note that pattern matching a list requires parentheses (i.e. `(x:xs)` instead of just `x:xs`). – Code-Apprentice Jan 22 '16 at 18:46