Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 292
Mark Karavan Avatar asked Dec 28 '25 04:12

Mark Karavan


2 Answers

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)

  • Type Families which I showed you
  • functional dependencies which Cactus did

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"
like image 51
Random Dev Avatar answered Dec 31 '25 00:12

Random Dev


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.

like image 41
Cactus Avatar answered Dec 30 '25 22:12

Cactus



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!