Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it square check

Tags:

haskell

I am trying to write function to check if the argument is square of integer:

isSquare :: Int -> Bool
isSquare x = truncate(sqrt(x)) * truncate(sqrt(x)) == x

When I loading the function I get the error:

Prelude> :load "some.hs"
[1 of 1] Compiling Main             ( some.hs, interpreted )

some.hs:2:13:
    No instance for (RealFrac Int)
      arising from a use of `truncate' at some.hs:2:13-29
    Possible fix: add an instance declaration for (RealFrac Int)
    In the first argument of `(*)', namely `truncate (sqrt (x))'
    In the first argument of `(==)', namely
        `truncate (sqrt (x)) * truncate (sqrt (x))'
    In the expression: truncate (sqrt (x)) * truncate (sqrt (x)) == x

some.hs:2:22:
    No instance for (Floating Int)
      arising from a use of `sqrt' at some.hs:2:22-28
    Possible fix: add an instance declaration for (Floating Int)
    In the first argument of `truncate', namely `(sqrt (x))'
    In the first argument of `(*)', namely `truncate (sqrt (x))'
    In the first argument of `(==)', namely
        `truncate (sqrt (x)) * truncate (sqrt (x))'
Failed, modules loaded: none.

But if i try to execute:

Prelude> truncate(sqrt(9))*truncate(sqrt(9))==9
True

all is fine.

Why I get the error and how to fix it ?

like image 361
ceth Avatar asked Dec 08 '22 00:12

ceth


1 Answers

You're getting the errors because of type mismatches. The type of sqrt is sqrt :: Floating a => a -> a, and the type of truncate is truncate :: (RealFrac a, Integral b) => a -> b. The former says that sqrt takes as input any floating-point number, and returns one of the same type as output; the latter says it can truncate any real fractional number1 into any integral number. However, you assert that x is an Int, and an Int isn't a floating-point number. Thus, the second error: "No instance for (Floating Int) arising from a use of `sqrt'". This says that because of sqrt x, it wanted Int to be a floating-point number, but there's no definition for that. Your first error is similar: since sqrt :: Floating a => a -> a, its output is the same as its input, so you're trying to call truncate on an integer. This of course makes no sense, since Int is not a RealFrac, and that's why you get the first error. Fixing this is easy:

isSquare :: Int -> Bool
isSquare x = let x' = truncate $ sqrt (fromIntegral x :: Double) in x'*x' == x

The fromIntegral function has the type fromIntegral :: (Integral a, Num b) => a -> b; it can convert any integral number into any number at all. This is why we need to tell Haskell that we want it to produce a Double; it'd default to that anyway, but it's nice to be clear (though not necessary). Double is an instance both of Floating and RealFrac, so you can sqrt and truncate it. I also rearranged your code a little; the way it is up there is how I'd write it, since this way we only compute the truncation and sqrt once. Also, note that if you remove the type signature, Haskell will infer the more general type isSquare :: Integral a => a -> Bool, since you never assume that x is precisely an Int.

The reason that truncate(sqrt(9))*truncate(sqrt(9))==9 successfully returned True is because of the type of 9. You can ask GHCi to tell you this:

Prelude> :t 9
9 :: (Num t) => t

In Haskell, all integral numeric literals have the type Num t => t (9.0, or any number with a decimal point, has the type Fractional t => t). This means that they can be any kind of number at all, which is a good thing. Otherwise, 9 would have to just be an Int or Integer, and defining new number types—or even using both Int and Integer!2—would be a royal pain. Thus, when you write truncate(sqrt(9)), GHCi determines that 9 must be an instance of Floating (from sqrt) and RealFrac (from truncate), which it defaults to Double, making everything work. This defaulting is standard behavior for numeric types (it's why you could leave out the :: Double in my definition of isSquare), though not for anything else (except in GHCi, which extends it for convenience). Since 9 isn't just an Int, but x is, you don't need to convert 9, but you do need to convert x.


1: The difference between Floating and RealFrac is that, for instance, Complex Double is an instance of Floating but not RealFrac, and Rational is an instance of RealFrac but not Floating. Float and Double are instances of both.

2: In case you haven't come across this, the difference is that Int is finite-precision, and Integer is arbitrary-precision.

like image 150
Antal Spector-Zabusky Avatar answered Dec 14 '22 22:12

Antal Spector-Zabusky