10

I'm trying to make a simple Scheme interpreter in Haskell. As part of this, I'm implementing some primitive operators like number?, string? etc.

I have code like this:

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

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

And what I'd like is something like

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

In other words, I'd like to create the equivalent of isNumber by saying "isType Number". Is this possible somehow? I'm struggling to find anything similar in Google, maybe because I don't know what to call the situation.

Daniel Eliasson
  • 225
  • 1
  • 8
  • 4
    You should probably say "constructor" instead of "type" since, as it's written, this question doesn't even really make sense in the context of Haskell. You're representing Lisp types with Haskell constructors, but that isn't directly relevant to the Haskell question it results in. – ehird Jan 21 '12 at 23:30
  • Data.Typeable may be worth a look, but it doesn't appear that you need dynamic checks here. – ExternalReality Jan 21 '12 at 23:50
  • 1
    See here for [a Template Haskell solution](http://stackoverflow.com/questions/7213974/how-to-examine-a-quoted-data-constructor-name-in-template-haskell/7214422#7214422). With this you can do `isNumber = $(isA 'Number)` – hammar Jan 22 '12 at 00:04

1 Answers1

8

I'm assuming you have a type something like this:

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

...and you want a function that tests whether a LispVal value is a particular constructor (String, Number, &c.) based on some argument.

There's not really a straightforward, generic way to do this, unfortunately.

You could resort to string comparisons:

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

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

Or you could compare the types of two LispVals:

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

...and then create a dummy value to compare with for isType.

You could also make a "type" value and implement a sort of reflection on LispVals, then compare based on those:

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

Some variation on one of these approaches is probably your best option. There are other ways, based on more advanced features of Haskell, but they're probably not worth the hassle unless the interpreted language's types tie much more closely to Haskell's types.

C. A. McCann
  • 76,893
  • 19
  • 209
  • 302
  • Using the `C{}` pattern syntax might be a good idea for `getType` to reduce the boilerplate. (Same goes for `getTypeName` and `sameType`.) – ehird Jan 22 '12 at 00:21
  • 2
    `getType (Type _) = Type LispType`...the type of a type is a type type? o_O? – Dan Burton Jan 22 '12 at 02:42
  • Thank you for the excellent reply! I think the second option will be the easiest for now. It still needs a bit more explicit pattern matching than I was hoping for, but it'll save me a few lines at least. – Daniel Eliasson Jan 22 '12 at 13:23