Is it possible in Haskell to have a function that can take a polymorphic type and return a polymorphic type?
For example, I want a function that takes a value, and returns an Int if the value is of type Foo and a String if it is of type Bar
data Foo = One | Two | Three deriving (Show, Read)
data Bar = This | That | TheOther deriving (Show, Read)
doSomething :: Either Foo Bar -> Either Int String
doSomething var = if (typeOf var) == Int then 123 else "string"
Is something like this possible? If not, what is the best practice for routing to another function based on type?
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"
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"
You can put doSomething in a typeclass keyed on both the domain and the codomain types, with a functional dependency from the domain to the codomain:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-} -- if one of your `to` really is `String`
class DoSomething from to | from -> to where
doSomething :: from -> to
instance DoSomething Foo Int where -- ...
instance DoSomething Bar String where -- ...
Compared with the type family-based solution, this has the benefit that if the codomain also uniquely determines the domain, you can add another functional dependency to -> from. I don't think type families offer a good way of modelling that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With