Reputation: 89
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
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