Mark Karavan
Mark Karavan

Reputation: 2674

haskell polymorphic type functions

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

Answers (2)

Cactus
Cactus

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

Random Dev
Random Dev

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"

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"

Upvotes: 5

Related Questions