2010-12-29 7 views
15

Mi aspettavo che il seguente codice fallisse con un errore di tipo a causa della violazione di minBound e maxBound. Ma, come puoi vedere, passa attraverso senza segnalare un errore.Come si usa la classe di tipi Bounded in Haskell per definire un tipo con un intervallo in virgola mobile?

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

nel preludio ottengo questo

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

E al prompt ottengo questo:

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

Chiaramente non sto dichiarando Bounded correttamente, e sono sicuro che sto facendo qualcosa di sbagliato sintatticamente Non ci sono molte cose semplici su Google per quanto riguarda i tipografi limitati, quindi qualsiasi aiuto sarebbe molto apprezzato.

risposta

21

Questo non è quello che è Bounded. Bounded a definisce solo le funzioni minBound :: a e maxBound :: a. Non induce alcun controllo speciale o altro.

È possibile definire un tipo limitato utilizzando il cosiddetto costruttore intelligente . Cioè:

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

Quindi l'unico modo per fare un Probability è quello di utilizzare la funzione mkP fine (questo è fatto per voi quando si utilizza operazioni numeriche dato il nostro Num esempio), che controlla che l'argomento è in campo . A causa della lista di esportazione del modulo, al di fuori di questo modulo non è possibile costruire una probabilità non valida.

Probabilmente non il doppio rivestimento che stavi cercando, ma vabbè.

Per una maggiore componibilità, è possibile scomporre questa funzionalità creando un modulo BoundCheck anziché "Probabilità". Proprio come sopra, ad eccezione di:

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

questo modo è possibile ottenere la funzionalità si stava desiderando fu costruito nel per voi quando lei ha chiesto la questione.

Per eseguire questa roba derivante potrebbe essere necessario l'estensione del linguaggio {-# LANGUAGE GeneralizedNewtypeDeriving #-}.

+0

Molto utile, grazie mille. Una domanda: metti un'ellissi ("...") per definire i vari modi in cui mkP e mkBC interagiscono con gli operatori esistenti su cose di tipo Num. Suppongo che lo scopo di ciò sia di definire gli operatori aritmetici per cose di tipo Probability che continuano a far girare l'output attraverso mkP per fare il controllo dei limiti. – ramanujan

+0

@ramanujan, sì. Fondamentalmente, continua in quel modello. – luqui

+0

Nel caso in cui non si sappia dove cercare i metodi 'Num': http://hackage.haskell.org/packages/archive/base/4.2.0.2/doc/html/Prelude.html#t%3ANum – luqui

Problemi correlati