user1198582
user1198582

Reputation:

Haskell map with keys from different types

I have some types

data Foo = Foo
data Bar = Bar 
data Baz = Baz

I want to use them as keys for a Map. Is this possible, and if so, how?

Additonal context below:

I have an application that builds VMs. I have seperated the work into Phases. Currently I have this type

data CurrentPhase = PHASEONE
                  | PHASETWO
                  | PHASETHREE (deriving Eq,Ord)

So far so good right, no problems like what I mentioned above. However, I made a type class to describe operations that are phase specific

class PhaseOps phase where
  preValidate :: JobID -> phase -> Handler (Status)
  doPreProc :: JobID -> phase -> Handler (Status)
  updateConfig :: JobID -> phase -> Handler ()
  postValidate :: JobID -> phase -> Handler (Status)

in order for this to work, I had to create a new set of singleton data types to use for PhaseOps instances.

data PhaseOne = PhaseOne

.. and so on

now I have these singleton types, and CurrentPhase. I'd like to get rid of CurrentPhase (which I am using for a Map with CurrentPhase being the key), and use my singleton data types.

Upvotes: 3

Views: 721

Answers (1)

C. A. McCann
C. A. McCann

Reputation: 77374

The straightforward solution is to use keys of type Either Foo (Either Bar Baz). This gets verbose quickly as you add possible types and is a bit ugly anyway, so it often makes more sense to use a special-purpose equivalent such as:

data FooBarBaz = FooVal Foo | BarVal Bar | BazVal Baz

This is similar to combining them into one type directly, but trades off a bit more verbosity in the combined type for being able to still use the individual types elsewhere. This is a relatively common pattern; for instance, I've seen it often in types representing syntax trees, where a "top level declaration" type might take this form, with each kind of declaration being its own separate type.

Depending on the nature of your problem there might be other approaches that would be better, but the above is the only good general-purpose solution I can think of--if you don't like doing it this way, you'll need to more clearly specify why and elaborate somewhat on what you need these types to accomplish.


Edit in response to clarification:

As I mentioned in the comments on the question, PhaseOps looks a great deal like a class that wants to be a record of functions instead. Furthermore, if you have such a class, wanting a way to work with multiple instance types as if they were a single type is a very strong indication that it's time to step back and rethink your design.

Continuing with such a design almost always leads to either mucking about with Typeable, as Thomas M. DuBuisson mentions in the comments, or to mucking about with existential types (which is a well-known anti-pattern these days). It's true that such approaches are occasionally required, but are best avoided unless you can very clearly explain (even if only to yourself) why you need them. Otherwise, they create far more problems than they solve.

Incidentally, if you want to retain some of the benefits of separate types, I'd consider using your singleton types for phantom type tagging and/or hiding the constructor for the PhaseOps record and using a smart constructor that takes a CurrentPhase argument.

Upvotes: 4

Related Questions