Reputation: 2674
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?
Upvotes: 3
Views: 1083
Reputation: 27636
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.
Upvotes: 3
Reputation: 52290
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"
Upvotes: 5