2011-05-18 7 views
5

me gustaría escribir una función que toma tantocoincidencia de patrón donde el patrón se basa en un (función) parámetro

  • un constructor valor para un determinado tipo de datos algebraico, y
  • un valor real de ese mismo tipo,

y determina si el valor dado está "hecho de" el constructor dado. La coincidencia de patrones parece un ajuste natural para esto, pero el patrón para hacer coincidir tendría que ser un parámetro de función en lugar de un nombre de constructor codificado.

El código siguiente es lo que he intentado, pero GHC informa un error de análisis en la línea indicada.

¿Hay alguna manera de lograr esto?

data FooBar = Foo Int | Bar String 

-- Imagine that these are useful functions. 
processInt :: Int -> String 
processInt = show 
processString :: String -> String 
processString = id 

-- This should take one of the above functions and adapt it to operate on 
-- FooBar values of compatible "type". Values that match the given FooBar 
-- constructor should be "unwrapped" and passed to the given function. 
typeCheck :: (a -> FooBar) -> (a -> String) -> (FooBar -> Maybe String) 
typeCheck constructor func fooBar = case fooBar of 
    (constructor x) -> Just (func x) -- GHC says "Parse error in pattern: constructor" 
    _ -> Nothing 

-- Define processing functions that operate on FooBars. 
processFoo :: FooBar -> Maybe String 
processFoo = typeCheck Foo processInt 
processBar :: FooBar -> Maybe String 
processBar = typeCheck Bar processString 

Respuesta

3

Idea interesante. Me pregunto qué estás tratando de hacer, ya que este es un problema de coincidencia de patrones bastante inusual.

Por supuesto que puede hacerlo si se puede:

  • enumerar los constructores del tipo
  • tener igualdad en los elementos del tipo

Al igual que (rompo a cabo la aplicación de f parte, ya que es ortogonal):

wasBuilt :: Eq t => (t -> Either t t) --^the constructor 
       -> Either t t   --^a value 
       -> Maybe t    --^the transformed result 

wasBuilt k v = case v of 
    Left x | v == k x -> Just x 
    Right x | v == k x -> Just x 
    _      -> Nothing 

Pero el Hay un montón de repetición. Este problema grita "genéricos" en mí. Pruebe con un enfoque diferente, y refleje el constructor en los datos, luego haga coincidir genéricamente esos datos, tal vez. Esto le permitirá tratar a los constructores como valores, en lugar de funciones.


Esto es más o menos lo que estaba pensando, pero tenga en cuenta que esta es una técnica avanzada. explícita patrón correspondiente de una AST regular es mucho, mucho más idiomática:

import Generics.SYB 

-- only works for unary constructors 
sameConstructor :: (Data a, Data b) => (a -> b) -> b -> Bool 
sameConstructor k v = toConstr v == toConstr (k undefined) 

> sameConstructor (Left :: Char -> Either Char Char) (Right 'x') 
False 

> sameConstructor (Left :: Char -> Either Char Char) (Left 'x') 
True 

> sameConstructor (:[]) "haskell" 
True 
+0

Es una calculadora RPN (programa de práctica) que puede tener una mezcla de diferentes tipos (por ejemplo, números y cadenas) en la pila. La mayoría de las funciones que operan en la pila comenzarán mostrando algunos valores, verificando que sean del tipo correcto para la operación e informando un error si no es así. Estoy tratando de factorizar esa comprobación de tipos para evitar la duplicación de código. – Wyzard

+0

¡Ah! Será mucho más fácil si codifica las etiquetas de tipo como valores simples (por ejemplo, valores 'Int'), en lugar de como funciones de constructor. Eso es lo que hace que tu ejemplo sea tan complicado: la coincidencia de patrones en las funciones es sencillamente difícil. –

+0

Gracias, consideraré usar etiquetas de tipo numérico en su lugar. Como mi conjunto de tipos es relativamente limitado, también podría hacer una familia de funciones como 'typeCheckInt',' typeCheckString', etc. con el constructor apropiado codificado en cada una.Como se trata de un programa de práctica, pensé que podría intentar algo un poco más desafiante, pero parece que me he mordido más de lo que puedo masticar. Estoy familiarizado con los genéricos en el contexto de C++ y Java, pero no en el contexto de Haskell. – Wyzard

2

Es posible que desee ver en Type-safe pattern combinators perla funcional. Si bien no se combina tan bien con la sintaxis de concordancia de patrones de Haskell, sí le permite tener la modularidad de patrones compostables de primera clase, si eso es lo que necesita (es decir, si la capacidad de compilación adicional supera las molestias sintácticas).

Cuestiones relacionadas