I've started learning Haskell and I've been reading "Learn You a Haskell for Great Good". I read up to halfway through the Modules chapter. A friend showed me codewars and I decided to put some of what I've learnt to the test.
I am trying to make a function which returns a Boolean of whether the given Integral is a power of 4. Here is the code.
module PowerOfFour where
isWhole n = fromIntegral (round n) == n
isPowerOf4 :: Integral n => n -> Bool
isPowerOf4 4 = True
isPowerOf4 x = if x < 4 then
False
else
if isWhole (x / 4) then
isPowerOf4 (truncate (x / 4))
else
False
This is the error message I get.
/tmp/haskell11524-8-kke52v/PowerOfFour.hs:10:12:
Could not deduce (RealFrac n) arising from a use of `isWhole'
from the context (Integral n)
bound by the type signature for
isPowerOf4 :: Integral n => n -> Bool
at /tmp/haskell11524-8-kke52v/PowerOfFour.hs:5:15-37
Possible fix:
add (RealFrac n) to the context of
the type signature for isPowerOf4 :: Integral n => n -> Bool
In the expression: isWhole (x / 4)
In the expression:
if isWhole (x / 4) then isPowerOf4 (truncate (x / 4)) else False
In the expression:
if x < 4 then
False
else
if isWhole (x / 4) then isPowerOf4 (truncate (x / 4)) else False
/tmp/haskell11524-8-kke52v/PowerOfFour.hs:10:23:
Could not deduce (Fractional n) arising from a use of `/'
from the context (Integral n)
bound by the type signature for
isPowerOf4 :: Integral n => n -> Bool
at /tmp/haskell11524-8-kke52v/PowerOfFour.hs:5:15-37
Possible fix:
add (Fractional n) to the context of
the type signature for isPowerOf4 :: Integral n => n -> Bool
In the first argument of `isWhole', namely `(x / 4)'
In the expression: isWhole (x / 4)
In the expression:
if isWhole (x / 4) then isPowerOf4 (truncate (x / 4)) else False
What am I doing wrong?
I used fromIntegral in isWhole to fix a similar error and isWhole now works fine individually. However, I can't seem to get rid of these errors in isPowerOf4. I've tried the possible fixes GHC provides, but I probably wasn't doing it right.
I'd rather keep the type signature of the isPowerOf4 function, as that is provided by codewars, so I'm guessing it's a requirement.
In Haskell the /
operator is for fractional types not integral ones. For integral types you should use div
(you can use it infix by surrounding it in backticks). You can also use mod
or rem
to find the remainder, like %
in typical languages (they are different for negative numbers)
isPowerOf4 :: Integral n => n -> Bool
isPowerOf4 4 = True
isPowerOf4 x = if x < 4
then False
else
if x `mod` 4 == 0
then isPowerOf4 (x `div` 4)
else False
As @leftaroundabout explained /
is floating point division and the results may be object to rounding error. Therefore your is isWhole
function is not guarantied to work correctly in all cases.
A better approach would be to use integer division and modulo, as suggested by @Jubobs.
div
is integer division it gives you the result of a division with out the remainder. That's the same as you expect from truncate (x / 4)
. This should be rewritten as div x 4
, so it's not relying on floating point numbers.
For isWhole
you can use modulo this is the rest of an integer division. (eg mod 5 4 == 1
) If it is zero there is no remainder, the result is a whole number. Instead of isWhole (x / 4)
you can use mod x 4 == 0
. Both div
and mod
work exact so there is not danger of rounding errors.
Haskell never, ever does implict type conversions. That may seem rather obdurate to most programmers, but actually it is one of the reasons why Haskell's type system works so great. Completely shunning conversions makes many things much simpler, and it's pretty prerequisite for full bidirectional type inference.
In your case, the problem is in x / 4
. A division, and you seem expect the result to be possibly noninteger. But it must be integer, as long as it has an integral type 1 ! So you need to explicitly convert to a fractional type 2 first. The simplest way is indeed the fromIntegral
function you've already discovered:
if isWhole $ fromIntegral x / 4 then ...
To make it clear: that's parsed as if isWhole ( (fromIntegral x)/4 ) then
. At this point you may wonder: if I just said Haskell never implicitly converts stuff, then why is it fine to divide by the integer literal 4
? The answer to that is, Haskell doesn't have integer literals! It only has various numeric literals . A whole-number literal doesn't have any particular type, it is polymorphic (ie, it requests from the context what type is expected here , and than behaves as that type – provided that the type is in the Num
class).
1 C “solves” this problem by making integer division trucate the result... which leads to a load of problems, probably more than you'd get with implicit conversion.
2 “Some fractional type” means, in Haskell, it'll default to Double
. That type is very fast, but it's also not entirely unproblematic because floating-point numbers are inherently inexact. I'm not sure right now if this would be a problem in your application; because of the ==
comparison it might well be. Basically, you need to specify the type of isWhole
to prevent that, as isWhole :: Rational -> Bool
. The Rational
type represents noninteger numbers as exact fractions.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.