2010-12-29 9 views
15

Esperaba que el siguiente código fallara con un error de tipo debido a la violación de minBound y maxBound. Pero, como puede ver, se procesa sin marcar un error.¿Cómo se usa la clase de tipo delimitada en Haskell para definir un tipo con un rango de coma flotante?

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

en el preludio recibí este

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

Y en el indicador me sale esto:

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

Claramente no estoy declarando acotada correctamente, y estoy seguro de que Estoy haciendo algo mal sintácticamente. No hay muchas cosas simples en Google sobre clases de tipos limitadas, por lo que cualquier ayuda sería muy apreciada.

Respuesta

21

Eso no es para lo que Bounded es. Bounded a solo define las funciones minBound :: a y maxBound :: a. No induce ningún control especial ni nada.

Puede definir un tipo delimitado mediante el denominado constructor inteligente. Es decir:

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

Así que la única manera de hacer un Probability es utilizar la función mkP con el tiempo (esto se hace para que cuando se utiliza operaciones numéricas dado nuestro Num ejemplo), que comprueba que el argumento está en el rango . Debido a la lista de exportación del módulo, fuera de este módulo no es posible construir una probabilidad inválida.

Probablemente no sea el de dos líneas que estabas buscando, pero bueno.

Para mayor capacidad de compilación, puede factorizar esta funcionalidad haciendo un módulo BoundCheck en lugar de `Probabilidad. Al igual que el anterior, excepto:

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

De este modo se puede obtener la funcionalidad que deseabas fue construido en para que cuando hizo la pregunta.

Para hacer esto puede necesitar la extensión de idioma {-# LANGUAGE GeneralizedNewtypeDeriving #-}.

+0

Muy útil, muchas gracias. Una pregunta: pones una elipsis ("...") para definir las diversas formas en que mkP y mkBC interactúan con operadores existentes en cosas de tipo Num. Supongo que el propósito de eso es definir operadores aritméticos para cosas del tipo de probabilidad que siguen ejecutando el resultado a través de mkP para hacer la comprobación de límites. – ramanujan

+0

@ramanujan, sí. Básicamente solo continúa en ese patrón. – luqui

+0

En caso de que no sepa dónde buscar los métodos 'Num': http://hackage.haskell.org/packages/archive/base/4.2.0.2/doc/html/Prelude.html#t%3ANum – luqui

Cuestiones relacionadas