## Why are floatRange, floatRadix and floatDigits functions?

According to Hackage, these functions of the `RealFloat`

class are

... constant function[s] ...

If they always remain at the same value, no matter the argument, as suggested by this description, why not simply use:

class (RealFrac a, Floating a) => RealFloat a where floatRadix :: Integer floatDigits :: Int floatRange :: (Int, Int) ...

The `RealFloat`

class is very old. Back when it was designed, nobody had worked out really good ways to pass extra type information to a function. At that time it was common to take an argument of the relevant type and expect the user to pass `undefined`

at that type. As leftaroundabout explained, GHC now has extensions that do this pretty nicely most of the time. But before `TypeApplications`

, two other techniques were invented to do this job more cleanly.

To disambiguate without GHC extensions, you can use either proxy passing or newtype-based tagging. I believe both techniques were given their final forms by Edward Kmett with a last polymorphic spin by Shachaf Ben-Kiki (see Who invented proxy passing and when?). Proxy passing tends to give an easy-to-use API, while the newtype approach can be more efficient under certain circumstances. Here's the proxy-passing approach. This requires you to pass an argument of some type. Traditionally, the caller will use `Data.Proxy.Proxy`

, which is defined

data Proxy a = Proxy

Here's how the class would look with proxy passing:

class (RealFrac a, Floating a) => RealFloat a where floatRadix :: proxy a -> Integer floatDigits :: proxy a -> Int ...

And here's how it would be used. Note that there's no need to pass in a value of the type you're talking about; you just pass the proxy constructor.

foo :: Int foo = floatDigits (Proxy :: Proxy Double)

To avoid passing a runtime argument at all, you can use tagging. This is often done with the `tagged`

package, but it's quite easy to roll your own too. You could even reuse `Control.Applicative.Const`

, but that doesn't communicate the intention so well.

newtype Tagged t a = Tagged { unTagged :: a }

Here's how the class would look:

class (RealFrac a, Floating a) => RealFloat a where floatRadix :: Tagged a Integer floatDigits :: Tagged a Int ...

And here's how you'd use it:

foo :: Int foo = unTagged (floatDigits :: Tagged Double Int)

Your proposed non-function methods would have type floatRadix' :: RealFloat a => Integer floatDigits' :: RealFloat a => Int Those are ambiguous types: there is Your proposed non-function methods would have type. floatRadix' :: RealFloat a => Integer floatDigits' :: RealFloat a => Int Those are ambiguous types: there is an a type variable, but it doesn't actually appear to the right of the => and thus can't be inferred from the context.

Your proposed non-function methods would have type

floatRadix' :: RealFloat a => Integer floatDigits' :: RealFloat a => Int ...

Those are *ambiguous types*: there is an `a`

type variable, but it doesn't actually appear to the right of the `=>`

and thus can't be inferred from the context. Which is, in standard Haskell, really the *only* way you can infer such a type variable: local type signatures also can only to the signature head, not the constraint. so whether you write `(floatDigits' :: Int)`

or `(floatDigits' :: RealFloat Double => Int)`

, it won't actually work – the compiler can't infer that you mean the `instance RealFloat Double`

version of the method.

class RealFloat' a where floatDigits' :: Int instance RealFloat' Double where floatDigits' = floatDigits (0 :: Double)

*Main> floatDigits' :: Int <interactive>:3:1: error: • No instance for (RealFloat' a0) arising from a use of ‘floatDigits'’ • In the expression: floatDigits' :: Int In an equation for ‘it’: it = floatDigits' :: Int *Main> floatDigits' :: RealFloat Double => Int <interactive>:4:1: error: • Could not deduce (RealFloat' a0) arising from a use of ‘floatDigits'’ from the context: RealFloat Double bound by an expression type signature: RealFloat Double => Int at :4:17-39 The type variable ‘a0’ is ambiguous • In the expression: floatDigits' :: RealFloat Double => Int In an equation for ‘it’: it = floatDigits' :: RealFloat Double => Int

For this reason, Haskell does not allow you to write methods with ambiguous type in the first place. Actually trying to compile the class as I wrote it above gives this error message:

• Could not deduce (RealFloat' a0) from the context: RealFloat' a bound by the type signature for: floatDigits' :: forall a. RealFloat' a => Int at /tmp/wtmpf-file3738.hs:2:3-21 The type variable ‘a0’ is ambiguous • In the ambiguity check for ‘floatDigits'’To defer the ambiguity check to use sites, enable AllowAmbiguousTypesWhen checking the class method: floatDigits' :: forall a. RealFloat' a => Int In the class declaration for ‘RealFloat'’

The highlighted line cites however a GHC extension that says "it's ok, I know what I'm doing". So if you add `{-# LANGUAGE AllowAmbiguousTypes #-}`

to the top of the file with the `class RealFloat'`

in it, the compiler will accept that.

What's the point though, when the instance can't be resolved at the use site? Well, it *can* actually be resolved, but only using another pretty new GHC extension:

*Main> :set -XTypeApplications *Main> floatDigits' @Double 53

These functions are: take, drop, 1 1, length, splitAt, and replicate. truncate, round, ceiling, floor), RealFloat (floatRadix, floatDigits, floatRange, decodeFloat, The functions floatRadix, floatDigits, and floatRange give the parameters of a floating-point type: the radix of the representation, the number of digits of this radix in the significand, and the lowest and highest values the exponent may assume, respectively.

The problem with this is that you would construct the functions for multiple instances, like:

instance RealFloat Float where -- ... floatRadix = 2 floatDigits = 24 floatRange = (-125, 128) instance RealFloat Double where -- ... floatRadix = 2 floatDigits = 53 floatRange = (-1021, 1024)

But now it creates a problem when you query for example `floatDigits`

: for what instance should we take? The one for `Float`

, or the one for `Double`

(or another type)? All of these are valid candidates.

By using an `a`

parameter, we can make a disambiguation, for example:

Prelude> floatDigits (0 :: Float) 24 Prelude> floatDigits (0 :: Double) 53

but it holds that the value of the parameter does not matter, for example:

Prelude> floatDigits (undefined :: Float) 24 Prelude> floatDigits (undefined :: Double) 53

The functions floatRadix, floatDigits, and floatRange give the parameters of a floating-point type: the radix of the representation, the number of digits of this radix The functions floatRadix, floatDigits, and floatRange give the parameters of a floating-point type: the radix of the representation, the number of digits of this radix in the significand, and the lowest and highest values the exponent may assume, respectively.

233 flist, 146 Float, 19, 233 floatapproxRational, 241 floatdigits, 241 Floating, 236 19 floatingToBational, 240 floatproperFraction, 241 floatradix, 241 floatrange, 243, 244 fromRealFrac, 232 fst, 232 function application, 125 function exit, 7 Why are floatRange, floatRadix and floatDigits functions? 6 Why doesn't GHCi allow type arguments for this version of join? 5 Are there nasalized nasal consonants?

The functions floatRadix, floatDigits, and floatRange give the parameters of a floating-point type: the radix of the representation, the number of digits of this radix 7 Why are floatRange, floatRadix and floatDigits functions? 6 Why doesn't GHCi allow type arguments for this version of join? 5 Are there nasalized nasal consonants?

Efficient, machine-independent access to the components of a floating-point number. Minimal complete definition. floatRadix, floatDigits, floatRange, decodeFloat, Syntax: decode-float float significand, exponent, sign. scale-float float integer scaled-float. float-radix float float-radix. float-sign float-1 &optional float-2 signed-float