nutbunny
nutbunny

Reputation: 89

How to display function output as a list, [a], instead of a string, show [a], in context of the example code below

I am starting to learn some programming and I have been given the following exercise:

"Create a function named divisors that takes an integer n > 1 and returns an array with all of the integer's divisors(except for 1 and the number itself), from smallest to largest. If the number is prime return the string '(integer) is prime'. Hint: Use divisors :: (Show a, Integral a) => a -> Either String [a]"

I do not yet understand how to employ the Either type, so in the interim I have decided to begin tackling the problem in baby steps.

Since part of the exercise requires constructing a function to distinguish between prime numbers and non-prime numbers, I decided to first create a provisional function: if the number (a) is not prime, I must display the list of its divisors [1..a]. If the number (a) is prime, I must display the string "(a) is prime".

The following code works:

divisors a = if length [i | i <- [1..a], mod a i == 0 ] > 2
             then show [i | i <- [1..a], mod a i == 0 ]
             else show a ++ " is prime"

Some output of this function is:

[1 of 1] Compiling Main             ( program.hs, interpreted )
Ok, one module loaded.
*Main> divisors 1
"1 is prime"
*Main> divisors 2
"2 is prime"
*Main> divisors 3
"3 is prime"
*Main> divisors 4
"[1,2,4]"
*Main> divisors 5
"5 is prime"
*Main> divisors 6
"[1,2,3,6]"
*Main> divisors 7
"7 is prime"

However, I do need to display the list as a list [1..a] and not a string "[1..a]". I thus erased (show) from the (then) statement:

divisors a = if length [i | i <- [1..a], mod a i == 0 ] > 2
             then [i | i <- [1..a], mod a i == 0 ]
             else show a ++ " is prime"

but this throws up an error:

program.hs:11:42: error:
    * No instance for (Integral Char) arising from a use of `mod'
    * In the first argument of `(==)', namely `mod a i'
      In the expression: mod a i == 0
      In a stmt of a list comprehension: mod a i == 0
   |
11 | divisors a = if length [i | i <- [1..a], mod a i == 0 ] > 2
   |                                          ^^^^^^^
Failed, no modules loaded.

I do not understand exactly what went wrong and need someone to explain how to display the output of non-prime numbers as a list instead of a string representation of the list.

I do not mind if someone wants to explain how to answer the original exercise question employing the Either type in the solution, if they are willing to explain how to re-formulate my function to employ the Either type properly.

EDIT: After reading some responses, I attempted the following:

divisors :: (Show a, Integral a) => a -> Either String [a]
divisors a = if length [i | i <- [2..a], mod a i == 0] > 1
             then Right [i | i <- [2..a-1], mod a i == 0]
             else Left (show a ++ " is prime")

and it seems to be working!

Thank you for the suggestions.

Upvotes: 3

Views: 112

Answers (1)

K. A. Buhr
K. A. Buhr

Reputation: 50864

From @RobinZigmond: Well you've discovered why you need Either. Haskell has a strong static type system, and a function needs to return values of a specific type. It's not possible for a function to do as you want and return a string on some inputs and a list of integers on other inputs (as it would be in a dynamic language). But that is what Either is for. A value of type Either a b is either Left x where x is a value of type a, or Right y where y is a value of type b. Hopefully you can now see why this would be useful for your case.

From @chi: the construct

if condition then x else y

requires x and y to be of the same type. If they are not, say:

x :: TypeX
y :: TypeY

you can convert both to Either TypeX TypeY as follows:

if condition then Left x else Right y

Since now the two if branches have the same type Either TypeX TypeY, it type checks, and that type will be the type of the value resulting from the if.

From the original poster, @nutbunny: After reading some responses, I attempted the following:

divisors :: (Show a, Integral a) => a -> Either String [a]
divisors a = if length [i | i <- [2..a], mod a i == 0] > 1
             then Right [i | i <- [2..a-1], mod a i == 0]
             else Left (show a ++ " is prime")

and it seems to be working!

Thank you for the suggestions!

Upvotes: 5

Related Questions