Reputation:
I'm trying to create a basic function to test primality of an integer in Haskell. I have code which will work in an ad hoc sense, but continue to get an error message when I try to pass it to a function. Note that I'm writing the definitions directly in GHCi, using :{
and :}
.
The idea is to create a list of N modulo {all integers up to a rounded sqrt (N)}, and then test the resulting list for a zero other than the initial index. The following four functions all work:
rndRoot :: (Floating a, Integral c, RealFrac a) => a -> c
rndRoot = round . sqrt
oneToRndRoot :: (Floating a, Integral t, RealFrac a) => a -> [t]
oneToRndRoot x = [1..rndRoot(x)]
modulo x y
| n < 0 = x
| otherwise = modulo n y
where n = x - y
mapMod x = map (modulo x)
This also works:
mapMod 49 (oneToRndRoot 49)
However, while the repl accepts this definition without complaint...
mapModToRndRoot x = mapMod x $ oneToRndRoot x
... it spits out the following error message when I try to use it:
Prelude> mapModToRndRoot 39
<interactive>:475:1:
No instance for (Floating a0) arising from a use of ‘it’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
instance Floating Double -- Defined in ‘GHC.Float’
instance Floating Float -- Defined in ‘GHC.Float’
In the first argument of ‘print’, namely ‘it’
In a stmt of an interactive GHCi command: print it
The ad hoc solution which seems to work fine is just to use two arguments rather than repeat the same one
mapModToRndRoot2 x y = map (modulo x) (oneToRndRoot y)
Prelude> mapModToRndRoot2 33 33
[0,1,0,1,3,3]
Upvotes: 4
Views: 307
Reputation: 780
Inspecting the type of mapModToRndRoot
give the following:
mapModToRndRoot :: (Floating a, Integral a, RealFrac a) => a -> [a]
So, to call mapModToRndRoot 39
, we'd need to assign a numeric type to the literal 39
that is an instance of RealFrac
, Integral
, and Floating
. However, none of the Prelude numeric types satisfy all three of those constraints, so we get a compile error.
On the other hand, mapMod 49 (oneToRndRoot 49)
works just fine. Notice the type of the below lambda:
\x y -> mapMod x (oneToRndRoot y)
:: (RealFrac a, Integral b, Floating a) => b -> a -> [b]
By using two literals, GHC is able to assign different types to each of them in order to satisfy the type class constrains.
Edit: Here is a solution suggested by @duplode:
rndRoot :: (Integral a) => a -> a
rndRoot = round . sqrt . fromIntegral
My original suggestion fromIntegral . round . sqrt
is susceptible to all the usual wonkiness that comes with using floating point arithmetic.
Edit: As @WarrickMacmillan points out, the signature of oneToRndRoot
must also be changed. Below is the entire working program fully annotated.
rndRoot :: Integral a => a -> a
rndRoot = round . sqrt . fromIntegral
oneToRndRoot :: Integral a => a -> [a]
oneToRndRoot x = [1..rndRoot(x)]
modulo :: (Ord p, Num p) => p -> p -> p
modulo x y
| n < 0 = x
| otherwise = modulo n y
where n = x - y
mapMod :: (Num b, Ord b) => b -> [b] -> [b]
mapMod x = map (modulo x)
mapModToRndRoot :: Integral a => a -> [a]
mapModToRndRoot n = mapMod n (oneToRndRoot n)
Upvotes: 4