2010-12-29 7 views
15

Oczekiwano, że poniższy kod zakończy się niepowodzeniem z błędem typu z powodu naruszenia wartości minBound i maxBound. Ale, jak widać, przechodzi on bez zgłaszania błędu.W jaki sposób użyć typu Bounded w Haskell, aby zdefiniować typ z zakresem zmiennoprzecinkowym?

{-# 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 

W Prelude uzyskać to

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

iw wierszu uzyskać to:

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

Oczywiście nie jestem oświadczając ograniczone właściwie, i jestem pewien, że Robię coś nieprawidłowo składniowo. W Google nie ma zbyt wielu prostych tematów związanych z czcionkami Bounded, więc każda pomoc będzie bardzo doceniana.

Odpowiedz

21

To nie jest to, do czego służy Bounded. Bounded a właśnie definiuje funkcje minBound :: a i maxBound :: a. Nie wywołuje żadnych specjalnych kontroli ani niczego.

Można zdefiniować typ ograniczony za pomocą tak zwanego inteligentnego konstruktora . Czyli:

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 
    ... 

więc jedynym sposobem, aby uczynić Probability jest użycie funkcji mkP ostatecznie (odbywa się to dla ciebie, gdy używasz operacji numerycznych biorąc pod uwagę nasze Num instancji), który sprawdza, czy argument jest w zasięgu . Ze względu na listę eksportu modułu poza tym modułem nie jest możliwe zbudowanie niepoprawnego prawdopodobieństwa.

Prawdopodobnie nie taki dwurzędowy, którego szukałeś, ale no cóż.

Dla dodatkowej kompozytywności, możesz zrekompensować tę funkcjonalność, tworząc moduł BoundCheck zamiast `Prawdopodobieństwa. Tak jak powyżej, z wyjątkiem:

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) 
    ... 

Dzięki temu możesz uzyskać funkcjonalność, o której marzysz, kiedy zostaniesz zapytany.

Aby to zrobić, możesz potrzebować rozszerzenia językowego {-# LANGUAGE GeneralizedNewtypeDeriving #-}.

+0

Bardzo pomocny, bardzo dziękuję. Jedno pytanie: wstawiasz elipsę ("..."), aby zdefiniować różne sposoby interakcji mkP i mkBC z istniejącymi operatorami na rzeczach Num. Przypuszczam, że celem tego jest zdefiniowanie operatorów arytmetycznych dla rzeczy typu Prawdopodobieństwo, które utrzymują wyjście przez mkP do sprawdzania ograniczeń. – ramanujan

+0

@ramanujan, tak. Zasadniczo kontynuuj w tym kierunku. – luqui

+0

Jeśli nie wiesz, gdzie szukać metod "Num": http://hackage.haskell.org/packages/archive/base/4.2.0.2/doc/html/Prelude.html#t%3ANum – luqui