Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Succinct code format when coercing data types in Haskell

Tags:

haskell

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?

like image 956
Penguino Avatar asked Dec 23 '22 22:12

Penguino


1 Answers

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.

like image 59
bradrn Avatar answered Jan 10 '23 05:01

bradrn