haskell - repeticion - tecnicas busqueda de patrones
¿Es posible exportar constructores para la coincidencia de patrones, pero no para la construcción, en Haskell Modules? (3)
Desde GHC 7.8 puede usar PatternSynonyms
para exportar patrones independientes de los constructores. Entonces, un análogo a la respuesta de @ Lambdageek sería
{-# LANGUAGE PatternSynonyms #-}
module ThingModule (Thing, pattern Foo, pattern Bar) where
pattern Foo a <- RealFoo a
pattern Bar a <- RealBar a
data Thing = RealFoo Thing | RealBar Int
y
{-# LANGUAGE PatternSynonyms #-}
module Main where
import ThingModule
doSomethingWithThing :: Thing -> Int
doSomethingWithThing (Foo x) = doSomethingWithThing x
doSomethingWithThing (Bar y) = y
Entonces parece constructores normales.
Si intenta utilizar Bar
para construir un valor, obtiene
Main.hs:9:32:
Bar used in an expression, but it''s a non-bidirectional pattern synonym
In the expression: Bar y
Un tipo de datos vainilla en Haskell tiene cero o más constructores, cada uno de los cuales desempeña dos roles.
En las expresiones, admite la introducción, es una función de cero o más argumentos para el tipo de datos.
En los patrones, admite la eliminación, es como una función del tipo de datos a Maybe (tupla de tipos de argumentos).
¿Es posible que la firma de un módulo oculte el primero mientras se expone el último?
El caso de uso es el siguiente: tengo un tipo, T, cuyos tipos de constructores solos a veces se pueden usar para construir tonterías. Tengo funciones de construcción que pueden usarse para crear instancias del tipo que garanticen que no son absurdas. Tendría sentido esconder los constructores en este caso, pero aún sería útil para los llamadores para poder coincidir con los patrones garantizados, sin sentido, que construyen con las funciones de construcción.
Sospecho que esto es imposible, pero en caso de que alguien tenga una forma de hacerlo, pensé que lo haría.
Lo mejor es ocultar los constructores y crear un conjunto de funciones de T -> Tal vez (Esto, Eso), T -> Tal vez (El, Otro, Cosa), etc.
No puedes. Pero si solo hay un número razonable de constructores para su tipo T, es posible que desee ocultar los constructores y, en su lugar, proporcionar una función que haga la coincidencia de patrones con el mismo espíritu que maybe :: b -> (a -> b) -> Maybe a -> b
.
Puede usar un tipo de vista y ver patrones para hacer lo que quiera:
module ThingModule (Thing, ThingView(..), view) where
data Thing = Foo Thing | Bar Int
data ThingView = FooV Thing | BarV Int
view :: Thing -> ThingView
view (Foo x) = FooV x
view (Bar y) = BarV y
Tenga en cuenta que ThingView
no es un tipo de datos recursivo: todos los constructores de valores se refieren a Thing
. Así que ahora puedes exportar los constructores de ThingView
de ThingView
y mantener el resumen de Thing
.
Use esto:
{-# LANGUAGE ViewPatterns #-}
module Main where
import ThingModule
doSomethingWithThing :: Thing -> Int
doSomethingWithThing(view -> FooV x) = doSomethingWithThing x
doSomethingWithThing(view -> BarV y) = y
La nota de flecha es GHC''s View Patterns . Tenga en cuenta que requiere un lenguaje pragma.
Por supuesto, no está obligado a usar patrones de vista, puede simplemente hacer todo el desugaring a mano:
doSomethingWithThing :: Thing -> Int
doSomethingWithThing = doIt . view
where doIt (FooV x) = doSomethingWithThing x
doIt (BarV y) = y
Más
De hecho, podemos hacerlo un poco mejor: no hay ninguna razón para duplicar todos los constructores de valores para Thing
y ThingView
module ThingModule (ThingView(..), Thing, view) where
newtype Thing = T {view :: ThingView Thing}
data ThingView a = Foo a | Bar Int
Continúa utilizándolo de la misma manera que antes, pero ahora las combinaciones de patrones pueden usar Foo
y Bar
.
{-# LANGUAGE ViewPatterns #-}
module Main where
import ThingModule
doSomethingWithThing :: Thing -> Int
doSomethingWithThing(view -> Foo x) = doSomethingWithThing x
doSomethingWithThing(view -> Bar y) = y