How do I use a Haskell type constructor as an enumeration?

365 views Asked by At

I am writing a program in Haskell that makes use of a lookup table.

eg.

type Table = [(Object, FilePath)]
data Object = Player { pName :: String }

I want to construct this in such a way that Player can be a lookup key:

[(Player, "data/players"), ...]

If I added another Object type Monster, my table might look like:

[(Player, "data/players"), (Monster, "data/mons"), ...]

However, my type definition of a Table suggests that I am looking up instantiated objects when, really, I just want to check if it's one type constructor or the other.

How do I go about doing this?

EDIT:

I suppose I want something like:

data ObjectType = Player | Monster | ...

but is there a way to avoid duplication of the data constructor and type constructor?

1

There are 1 answers

2
Ganesh Sittampalam On BEST ANSWER

You can't really do this in the way you describe. Because Player takes an argument (pName), the type of Player itself is String -> Object, so it won't fit in your Table type properly.

As suggested in your edit, you should probably make a separate enumeration type without arguments specifically for Table:

data ObjectType = PlayerType | MonsterType | ...

Depending on how the other constructors of Object will be defined, you might be able to avoid duplication, e.g.

data Object = Object { objectType :: ObjectType, name :: String }

but that does assume that every kind of Object will have exactly one name argument and nothing else.

EDIT:

On reflection, I wonder if having a lookup table structure makes sense in the first place. You could replace the table with this:

lookupPath :: Object -> String
lookupPath (Player {}) = "data/players"
lookupPath (Monster {}) = "data/mons"
...

This format will make it harder to do things like persisting the table to disk, but does exactly capture your intention of wanting to match on the object without its parameters.

(The Player {} format for the match is the best way to match on constructors that may acquire more arguments in future, as it saves you from having to update the matching code when this happens.)