2012-01-21 5 views
10

Estoy tratando de hacer un simple intérprete de esquema en Haskell. Como parte de esto, ¿estoy implementando algunos operadores primitivos como number ?, string? etc.Haskell función que toma un tipo y un valor y comprueba si el valor tiene ese tipo

tengo código como este:

isNumber :: [LispVal] -> LispVal 
isNumber ([Number n]) = Bool True 
isNumber   _ = Bool False 

isString :: [LispVal] -> LispVal 
isString ([String n]) = Bool True 
isString   _ = Bool False 

Y lo que me gustaría es algo así como

isType :: ?? -> [LispVal] -> LispVal 
isType (typeName [typeName n]) = Bool True 
isType      _ = Bool False 

En otras palabras, me gustaría crear el equivalente de ISNUMBER por diciendo "isType Number". ¿Es esto posible de alguna manera? Estoy luchando por encontrar algo similar en Google, tal vez porque no sé cómo llamar a la situación.

+4

Probablemente debería decir "constructor" en lugar de "tipo" ya que, como está escrito, esta pregunta ni siquiera tiene sentido en el contexto de Haskell. Está representando tipos de Lisp con constructores de Haskell, pero eso no es directamente relevante para la pregunta de Haskell que resulta. – ehird

+0

Data.Typeable puede valer la pena, pero no parece que necesite controles dinámicos aquí. – ExternalReality

+1

Consulte aquí para [una solución de plantilla Haskell] (http://stackoverflow.com/questions/7213974/how-to-examine-a-quoted-data-constructor-name-in-template-haskell/7214422#7214422). Con esto puedes hacer 'isNumber = $ (isA 'Number)' – hammar

Respuesta

8

Estoy suponiendo que tiene un tipo algo como esto:

data LispVal = String String | Number Double -- &c.... 

... y desea una función que comprueba si un valor LispVal es un constructor en particular (String, Number, & c.) basado en algún argumento

Desafortunadamente, no existe una forma sencilla y genérica de hacerlo.

Se podría recurrir a las comparaciones de cadenas:

getTypeName :: LispVal -> String 
getTypeName (String _) = "String" 
getTypeName (Number _) = "Number" 

isType :: String -> [LispVal] -> LispVal 
isType name [val] = Bool (name == getTypeName val) 
isType _ _ = Bool False 

O puede comparar los dos tipos de LispVal s:

sameType :: LispVal -> LispVal -> LispVal 
sameType (String _) (String _) = Bool True 
sameType (Number _) (Number _) = Bool True 
sameType _ _ = Bool False 

... y luego crear un valor ficticio de comparar por isType .

También puede hacer que un valor "tipo" y poner en práctica una especie de reflexión sobre LispVal s, a continuación, comparar basan en las que:

data LispType = LispString | LispNumber | LispType 

getType :: LispVal -> LispVal 
getType (String _) = Type LispString 
getType (Number _) = Type LispNumber 
getType (Type _) = Type LispType 

isType :: LispVal -> [LispVal] -> LsipVal 
isType t [v] = isEqual t (getType v) 
isType _ _ = Bool False 

alguna variación en uno de estos enfoques es probablemente su mejor opción. Hay otras maneras, basadas en funciones más avanzadas de Haskell, pero probablemente no valen la pena a menos que los tipos de lenguaje interpretado se vinculen mucho más estrechamente con los tipos de Haskell.

+0

Usar la sintaxis del patrón 'C {}' podría ser una buena idea para 'getType' para reducir el texto repetitivo. (Lo mismo ocurre con 'getTypeName' y' sameType'.) – ehird

+2

'getType (Type _) = Type LispType' ... ¿el tipo de un tipo es un tipo de tipo? o_O? –

+0

¡Gracias por la excelente respuesta! Creo que la segunda opción será la más fácil por ahora. Todavía necesita una coincidencia de patrón más explícita de lo que esperaba, pero al menos me salvará unas líneas. –

Cuestiones relacionadas