2011-05-18 3 views
5

vorrei scrivere una funzione che prende entrambepattern matching dove il modello è basato su (funzione) parametro

  • un costruttore valore per un certo tipo di dati algebrico, e
  • un valore effettivo dello stesso tipo,

e determina se il valore dato è "fatto da" il costruttore indicato. La corrispondenza tra i motivi sembra essere naturale per questo, ma il modello con cui confrontarsi dovrebbe essere un parametro di funzione invece di un nome di costruttore hard-coded.

Il codice seguente è quello che ho provato, ma GHC segnala un errore di analisi sulla linea indicata.

C'è un modo per realizzare questo?

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 

risposta

3

Idea interessante. Mi chiedo che cosa stai cercando di fare, dal momento che questo è un problema piuttosto insolito.

Si può certamente farlo se è possibile:

  • enumerare i costruttori del tipo
  • avere uguaglianza su elementi del tipo

Come così (mi rompere l'applicazione di f parte, dal momento che è ortogonale):

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 

Ma il c'è un sacco di piatti. Questo problema urla "generici" a me. Prova un approccio diverso e rifletti il ​​costruttore sui dati, quindi abbina genericamente su quei dati, forse. Ciò ti consentirà di trattare i costruttori come valori, anziché funzioni.


Ecco approssimativamente quello che stavo pensando, ma nota, questa è una tecnica avanzata. modello esplicito di corrispondenza su un AST regolare è molto, molto più idiomatica:

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

È una calcolatrice RPN (programma di esercitazione) che può avere una combinazione di diversi tipi (ad esempio numeri e stringhe) nello stack. La maggior parte delle funzioni che operano sullo stack inizieranno spuntando alcuni valori, verificando che siano del tipo giusto per l'operazione e riportando un errore in caso contrario. Sto cercando di calcolare quel tipo di controllo per evitare la duplicazione del codice. – Wyzard

+0

Ah! Sarà molto più semplice se codifichi i tag del tipo come valori semplici (ad esempio, i valori di Int), anziché come funzioni di costruzione. Questo è ciò che rende il tuo esempio così complicato: la combinazione di modelli sulle funzioni è difficile. –

+0

Grazie, prenderò in considerazione l'utilizzo di tag di tipo numerico. Dato che il mio set di tipi è relativamente limitato, potrei anche creare una famiglia di funzioni come 'typeCheckInt',' typeCheckString', ecc. Con il costruttore appropriato codificato in ciascuno di essi.Dato che questo è un programma di pratica, ho pensato di provare qualcosa di un po 'più impegnativo, ma suona come se avessi potuto masticare più di quanto possa masticare. Conosco i generici nel contesto di C++ e Java, ma non nel contesto di Haskell. – Wyzard

2

Si potrebbe desiderare di guardare Type-safe pattern combinators perla funzionale. Anche se non si mescola bene con la sintassi di pattern matching di Haskell, ti permette di avere la modularità di pattern composable di prima classe, se è quello che ti serve (cioè se la composabilità aggiunta supera l'inconveniente sintattico).