first the things you describe and the Either Int String signature seem not to match - I'll try what you describe first (choose the output-type by it's input type):
you can do something very similar to what I think you are trying with type-families:
{-# LANGUAGE TypeFamilies #-}
module SO where
data Foo = One | Two | Three deriving (Show, Read)
data Bar = This | That | TheOther deriving (Show, Read)
class PolyMap k where
type To k :: *
polyMap :: k -> To k
instance PolyMap Foo where
type To Foo = Int
polyMap _ = 123
instance PolyMap Bar where
type To Bar = String
polyMap _ = "string"
example:
λ> polyMap One
123
λ> polyMap That
"string"
some explanation
What I think you want is to have type-mappings/functions (there is no runtime check with typeOf out-of-the box and this would give you some nice type-checking support instead) and there are basically two ways to do it (I am aware of)
Both give you (among others) the means to have some way to say: look if I get type A I can say what some associated type B must be (Foo -> Int and Bar -> String)
This is a deep topic (borderline dependent-types ;)) but I think type-families with classes are not to hard to understand.
The idea I used is to have the class PolyMap that provides the polyMap function (you can have it named anything you want - doSomething, whatever) and there the output type depends on the input type using the To k mapping which is Int for Foo and String for Bar as stated in the instance-declarations.
the other one for your signature is even easier:
doSomething :: Either Foo Bar -> Either Int String
doSomething (Left _) = Left 123
doSomething (Right _) = Right "string"
example:
λ> doSomething (Left One)
Left 123
λ> doSomething (Right That)
Right "string"