As a raw beginner in Haskell I am attempting to get all my exercise code to compile without warning while using the -Wall option in the ghc compiler. I am also trying to understand the use of '$' and '.' to avoid excessive parentheses.
In the following code
module Helpers (intSqrt1, intSqrt2) where
intSqrt1 :: Int -> Int
intSqrt1 x = truncate $ sqrt $ fromIntegral x
intSqrt2 :: Int -> Int
intSqrt2 x = truncate ( sqrt (fromIntegral x) :: Double)
intSqrt1 gives the warning Defaulting the following constraints to type `Double'. I can suppress the warning by coercing the result from sqrt to Double (see intSqrt2), but only at the cost of adding two pairs of parentheses.
Is there a way to get the best of both worlds in this function: i.e. both succinct code and suppression of warnings?
What's happening here is that you're using fromIntegral
to convert an Int
to some type a
, you're using sqrt
to convert a
to a
, and you're using truncate
to convert a
back to Int
. From the constraints on those functions, GHC knows that a
must be Floating
and RealFrac
, but it doesn't know what a
is. To solve this, GHC maintains a set of defaulting rules; in this case, they state that any ambiguous type which is Floating
or RealFrac
is defaulted to Double
. Defaulting may not be the intended behaviour in all cases though, so GHC also prints a warning.
When you add the type signature, the ambiguity is removed, which is why the message disappears. Adding a type signature is a bit clunky though; is there a better way? Actually, there is! First, you need to enable the TypeApplications
extension by putting the following pragma at the top of your file:
{-# LANGUAGE TypeApplications #-}
This extension enables you to use the syntax @SomeType
as the first parameter of any function; if the function has any type variables in its signature, this specialises the first to SomeType
. (Subsequent uses specialise the second, third, fourth etc. type variable.) In this case, we have a choice of places to put the type application. We could put it on fromIntegral
:
intSqrt x = truncate $ sqrt $ fromIntegral @_ @Double x
(Note that fromIntegral
has two type variables, so we leave the first one to be inferred as Int
and only specialise the second one.)
Or we can put it on sqrt
:
intSqrt x = truncate $ sqrt @Double $ fromIntegral x
Or on truncate
:
intSqrt x = truncate @Double $ sqrt $ fromIntegral x
Any of these variants will solve the problem concisely.
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