2012-04-15 12 views
13

¿Cómo puedo agregar comprobaciones de entrada a los constructores de datos Haskell? Digamos que tengoComprobaciones de entrada en los constructores de datos Haskell

import Data.Time.Calendar 

data SchedulePeriod = SchedulePeriod { startDate :: Day 
    , endDate :: Day 
    , accrualStart :: Day 
    , accrualEnd :: Day 
    , resetDate :: Day 
    , paymentDate :: Day 
    , fraction :: Double } 
    deriving (Show) 

y yo quiero imponer una restricción startDate < endDate. ¿Hay alguna forma de hacerlo sin crear un tipo de datos abstracto?

+0

posible duplicado de [Cómo hacer un tipo con restricciones] (http://stackoverflow.com/questions/7978191/how-to-make-a-type-with-restrictions) – ehird

Respuesta

17

La forma estándar es utilizar un smart constructor que verifica la condición previa antes de crear el valor y no exportar el constructor real que utiliza. Por supuesto, esto está creando un tipo de datos abstracto, como dijiste.

La única manera de lograr esto sin un constructor inteligente sería realmente mal tipo hackery sistema (y no sería capaz de utilizar el estándar de tipo Day).

+0

¿Puedo usar parámetros con nombre en smart constructores? –

+0

Si quiere decir imitar la sintaxis del registro, desafortunadamente no. Sin embargo, podría definir otro registro para servir como entrada al constructor inteligente. Sin embargo, es probable que solo valga la pena la duplicación si el constructor hace algo más que validar la entrada (p.cálculo de campos auxiliares). – ehird

+3

@quant_dev Si la razón por la que no desea un tipo de datos abstracto es para que pueda coincidir con los patrones en los constructores, puede hacer el doble de constructores inteligentes e implementar destructores inteligentes (similar a 'quizás' y' cualquiera'). –

13

Acepte la respuesta de ehird. Solo escribo esto para poder explicar los destructores inteligentes que mencioné en un comentario y no puedo incluir la explicación en un comentario.

Digamos que usted tiene el tipo:

data T x y z = A | B x | C y z 

ehird ya se ha explicado cómo abstraer el constructor, que es sólo para proporcionar constructores "inteligentes". Como mencionaste, esto requiere ocultar los constructores y luego no puedes usarlos para emparejar patrones. Sin embargo, puede resolver esto utilizando un destructor "inteligente", que es equivalente a la coincidencia de patrones con cada posible constructor.

Para explicar esto, primero vamos a empezar con la forma en que escribíamos en función del tipo T si se expusieron los constructores:

myFunction :: T x y z -> d 
myFunction t = case t of 
    A  -> f1 
    B x -> f2 x 
    C y z -> f3 y z 

Sabemos por firma de tipo de función que los tipos de f1, f2, y f3 debe ser:

f1 :: d 
f2 :: x -> d 
f3 :: y -> z -> d 

Así que si tuviéramos que generalizar myFunction ser un destructor inteligente, que sólo tiene que pasar f1, f2 y f3 como parámetros a la misma:

smartDestructor :: d -> (x -> d) -> (y -> z -> d) -> t -> d 
smartDestructor f1 f2 f3 t = case t of 
    A  -> f1 
    B x -> f2 x 
    C y z -> f3 y z 

Así que si exporta smartDestructor, entonces la gente puede básicamente patrón-partido contra el tipo sin necesidad de acceder a los constructores.

Si alguna vez has usado los maybe o either funciones, antes, entonces usted ha utilizado un destructor inteligente, aunque en esos casos los constructores no están ocultos, por lo que se prestan principalmente como funciones de confort:

maybe :: b -> (a -> b) -> Maybe a -> b 
maybe f1 f2 m = case m of 
    Nothing -> f1 
    Just a -> f2 x 

either :: (a -> c) -> (b -> c) -> Either a b -> c 
either f1 f2 e = case e of 
    Left a -> f1 a 
    Right b -> f2 b 

En su caso, el propósito del destructor inteligente es solo para poder ocultar los constructores y no exponer los constructores.

+0

Gracias, muy esclarecedor. Desearía poder aceptar dos respuestas ... –

+3

@quant_dev Acabo de enterarme de que hay un nombre mejor para "destructores inteligentes". Se llama "codificación de iglesia" del tipo de datos y se refiere al hecho de que cualquier tipo de datos se puede representar como una función, y esta codificación funcional se denomina "codificación de iglesia". Para una muy buena explicación, revisa este enlace: http://web.archiveorange.com/archive/v/nDNOvgzSRV6TNq8KhCgZ –

+0

Gracias, leeré eso. –

Cuestiones relacionadas