0
votes

I'm trying to code a function that takes a list of string (containing actions to do on the lists inside the tuple) and a tuple of 2 lists of Int as parameters (testReturn). The function is supposed to execute an action on the Int lists, checks if the first list in the tuple is in ascending order, and then returns a boolean depending on the result.

This code doesn't compile, and I'm not sure about a few things.

My code :

testReturn :: [[Char]] -> ([a], [b]) -> Bool
testReturn [] b = orderedCheck $ fst b
testReturn (a:as) b
    | a == "sa" = testReturn as (saFunc $ fst b, snd b)

orderedCheck :: (Ord a) => [a] -> Bool
orderedCheck []  = True
orderedCheck [x] = True
orderedCheck (x:y:xs) = x <= y && orderedCheck (y:xs)

saFunc :: [a] -> [a]
saFunc x
    | length x <= 1 = x
saFunc (x:y:xs) = (y:x:xs)

I know that the orderedCheck and saFunc functions work

I'm concerned about :

  1. [[Char]] : Not sure if writing this is right or not, and not sure if it really means a list of strings.
  2. orderedCheck $ fst b : this is the part where I have a GHC error : "No instance for (Ord a) arising from a use of ‘orderedCheck’ ",I looked around and I can't understand what does it mean.
  3. I heard somewhere else that the use of fst and snd may be a bit edgy (is it?), but I don't know how to do without it.

Could you guys please help me understand my errors in order to fix my code? Thanks!

1
the a in testReturn has no restrictions, but it uses orderedCheck which impose Ord a, hence the errorlsmor
@Ismor by restrictions, do you mean like a type?Accipiter13
Try removing the type annotation from testReturn and use GHCi's :type command to ask it about the inferred type.Mark Seemann

1 Answers

0
votes
  1. [[Char]] : Not sure if writing this is right or not, and not sure if it really means a list of strings.

Yes, it does mean a list of strings - that is, the type [String]. This is the same as [[Char]] because in Haskell String and [Char] are type synonyms, literally different ways of referring to the exact same thing. So putting a pair of square brackets round each (to denote a list of that type) also leads to two equivalent types.

String is usually considered more idiomatic than [Char] when writing Haskell code, but it's not a big deal. (Even if you use String everywhere, the compiler might still use [Char] when reporting errors, so it's important to know about this equivalence.)

  1. orderedCheck $ fst b : this is the part where I have a GHC error : "No instance for (Ord a) arising from a use of ‘orderedCheck’ ",I looked around and I can't understand what does it mean.

It's exactly as @Ismor said in the comments - but as I have a little more space in an answer I'll try to give a slightly longer, clearer explanation.

You are calling orderedCheck with the value fst b. And orderedCheck, by your own (correct) type signature, has type (Ord a) => [a] -> Bool. That means its argument must be a list, whose elements are of a type a which satisfies the constraint Ord a.

But you are using it on fst b in testReturn. b is the second argument to the function, which from its type signature must be of type ([a], [b]). This means fst b must be of type [a] - which is a list, so so far so good. But as just mentioned, that type a must have an instance of Ord. And unfortunately we can't guarantee that, because your type signature - [[Char]] -> ([a], [b]) -> Bool - allows both a and b to be absolutely anything. If this code compiled, that type signature would allow me to call that function as, for example, testReturn ["Example"] ([putStrLn "foo"], []) - but putStrLn "foo" has type IO (), which certainly has no Ord instance. (Unless you provide one in your code, which doesn't sound like a great idea - but even if you did, you could easily find another type with no Ord instance which would lead to the same problem.)

The only way to fix this is to fix the type signature of testReturn - it can't actually accept any type as a, because that type must have an Ord instance. So include that requirement, and you'll be fine:

testReturn :: (Ord a) => [[Char]] -> ([a], [b]) -> Bool

(As @MarkSeemann notes, that's exactly the signature GHC would have inferred for you if you'd left out any type signature. I certainly don't recommend leaving off type signatures for top-level functions, but if you're ever not sure of what the signature should be, this technique of leaving it out and then asking GHCi to infer it for you with :t is almost guaranteed to give you what you want.)

  1. I heard somewhere else that the use of fst and snd may be a bit edgy (is it?), but I don't know how to do without it.

I'm not sure what "edgy" means in this context. It's certainly not "wrong", nor is it dangerous in the same way that head and tail are for lists - those functions will crash (at runtime) when given an empty list, whereas fst and snd will work fine for any 2-tuple (and if given a value that's not a 2-tuple, your code will fail to compile - so there is zero risk of a runtime crash).

However, it's not very elegant compared to pattern-matching, which here is rather like "destructuring" in other languages. I think most Haskellers would write your function like this instead, which is equivalent to your code and hopefully self-explanatory:

testReturn [] (b, _) = orderedCheck b
testReturn (a:as) (b, c)
    | a == "sa" = testReturn as (saFunc b, c)

(Note that this function appears incomplete, and will crash if called with a non-empty first argument whose first element is anything other than "sa".)