Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you use the Bounded typeclass in Haskell to define a type with a floating point range?

I expected the following code to fail with a type error due to violation of the minBound and maxBound. But, as you can see, it goes through without flagging an error.

{-# OPTIONS_GHC -XTypeSynonymInstances #-}
module Main where

type Probability = Float
instance Bounded Probability where
    minBound = 0.0
    maxBound = 1.0

testout :: Float -> Probability
testout xx = xx + 1.0

main = do
  putStrLn $ show $ testout 0.5
  putStrLn $ show $ testout (-1.5)
  putStrLn $ show $ testout 1.5

In the Prelude I get this

*Main> :type (testout 0.5)
(testout 0.5) :: Probability

And at the prompt I get this:

[~/test]$runhaskell demo.hs
1.5
-0.5
2.5

Clearly I'm not declaring Bounded properly, and I'm sure I'm doing something wrong syntactically. There isn't much simple stuff on Google regarding Bounded typeclasses, so any help would be much appreciated.

like image 383
ramanujan Avatar asked Dec 29 '10 20:12

ramanujan


People also ask

How do you define a type class in Haskell?

A typeclass defines a set of methods that is shared across multiple types. For a type to belong to a typeclass, it needs to implement the methods of that typeclass. These implementations are ad-hoc: methods can have different implementations for different types.

What is bounded in Haskell?

The Bounded class is used to name the upper and lower limits of a type. Ord is not a superclass of Bounded since types that are not totally ordered may also have upper and lower bounds. The.

What are Typeclasses used for?

Type classes are a powerful tool used in functional programming to enable ad-hoc polymorphism, more commonly known as overloading.

What are different data types in Haskell write command to find min and max value of each of these types?

For example, Int type data has maximum bound of "9223372036854775807" and minimum bound of "-9223372036854775808". The following code shows how Haskell determines the maximum and minimum bound of Int type. Now, try to find the maximum and minimum bound of Char, Float, and Bool types.


1 Answers

That's not what Bounded is for. Bounded a just defines the functions minBound :: a and maxBound :: a. It does not induce any special checking or anything.

You can define a bounded type using a so-called smart constructor. That is:

module Probability (Probability) where

newtype Probability = P { getP :: Float }
    deriving (Eq,Ord,Show)

mkP :: Float -> Probability
mkP x | 0 <= x && x <= 1 = P x
      | otherwise = error $ show x ++ " is not in [0,1]"

-- after this point, the Probability data constructor is not to be used

instance Num Probability where
    P x + P y = mkP (x + y)
    P x * P y = mkP (x * y)
    fromIntegral = mkP . fromIntegral
    ...

So the only way to make a Probability is to use the mkP function eventually (this is done for you when you use numeric operations given our Num instance), which checks that the argument is in range. Because of the module's export list, outside of this module is it not possible to construct an invalid probability.

Probably not the two-liner you were looking for, but oh well.

For extra composability, you could factor out this functionality by making a BoundCheck module instead of Probability. Just like above, except:

newtype BoundCheck a = BC { getBC :: a }
    deriving (Bounded,Eq,Ord,Show)

mkBC :: (Bounded a) => a -> BoundCheck a
mkBC x | minBound <= x && x <= maxBound = BC x
       | otherwise = error "..."

instance (Bounded a) => Num (BoundCheck a) where
    BC x + BC y = mkBC (x + y)
    ...

Thus you can get the functionality you were wishing was built in for you when you asked the question. 

To do this deriving stuff you may need the language extension {-# LANGUAGE GeneralizedNewtypeDeriving #-}.

like image 91
luqui Avatar answered Sep 28 '22 08:09

luqui