I'm learning Haskell and I'm trying to write a function to return a list of factors for a number. Here's what I have:
factors :: Int -> [Int]
factors n = [x | x <- [2..s], n `mod` x == 0]
where s = floor (sqrt n)
When I try to load the module in ghci
, I get two errors,
p003.hs:3:14:
No instance for (RealFrac Int)
arising from a use of `floor' at p003.hs:3:14-27
Possible fix: add an instance declaration for (RealFrac Int)
In the expression: floor (sqrt n)
In the definition of `s': s = floor (sqrt n)
In the definition of `factors':
factors n = [x | x <- [2 .. s], n `mod` x == 0]
where
s = floor (sqrt n)
p003.hs:3:21:
No instance for (Floating Int)
arising from a use of `sqrt' at p003.hs:3:21-26
Possible fix: add an instance declaration for (Floating Int)
In the first argument of `floor', namely `(sqrt n)'
In the expression: floor (sqrt n)
In the definition of `s': s = floor (sqrt n)
Failed, modules loaded: none.
Any suggestions?
The most basic way of defining a function in Haskell is to ``declare'' what it does. For example, we can write: double :: Int -> Int double n = 2*n. Here, the first line specifies the type of the function and the second line tells us how the output of double depends on its input.
The == is an operator for comparing if two things are equal. It is quite normal haskell function with type "Eq a => a -> a -> Bool". The type tells that it works on every type of a value that implements Eq typeclass, so it is kind of overloaded.
As the name suggests zipWith function in Haskell is used to zip the elements pausing to the function. With the zipWith function, we can pass our vales, zipWith always followed by one operator it can be anything on the basis of which it will zip the value of the argument and produce the result for us.
The parameter has type Int
, so you cannot calculate a square root for it. You need to convert it to a floating point type first, which you can do with fromIntegral. Unlike some other languages, Haskell does not automatically promote integers to floating point numbers (nor do any other automatic type conversions).
So change sqrt n
to sqrt (fromIntegral n)
.
The type of the sqrt
function is
sqrt :: (Floating a) => a -> a
You can check this by typing :t sqrt
in ghci.
Int
is not an instance of Floating
, which is why you're seeing the second error.
The cause of the first error is the same; checking :t floor
reveals that the type is:
floor :: (RealFrac a, Integral b) => a -> b
The function is expecting an instance of RealFrac
, and you're supplying an Int
.
Typing :info RealFrac
or :info Floating
reveals that neither has an instance for Int
, which is why the body of the error says
No instance for ... Int
The solution to this problem, is to make sure that the types are correct; they must be members of the proper type classes.
A simple way to do this is to use the fromIntegral
function, which :t
reveals is of type:
fromIntegral :: (Integral a, Num b) => a -> b
Using fromIntegral
is necessary because the incoming type is Int
, but the functions floor
and sqrt
operate on types RealFrac
and Floating
, respectively.
It's allowed because, as you can see from the type signature, fromIntegral
returns an instance of Num
, which includes both the RealFrac
and Floating
types. You can convince yourself of this by typing :info Num
and :info Float
into ghci, and viewing the output.
Making his change to your program would have the final result below, which should work as you want:
factors :: Int -> [Int]
factors n = [x | x <- [2..s], n `mod` x == 0]
where s = floor (sqrt $ fromIntegral n)
Two good resources for understanding exactly what's going on are the Haskell tutorial's sections on Type Classes and Numbers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With