A short search didn't help me to find the answer, so I started to doubt in its existance. The question is simple. I want to create a polymorphic function, something like this:
f :: String -> String
f s = show (length s)
f :: Int -> String
f i = show i
A function defined differently for different data types is meant. Is it possible? If yes, how?
Polymorphism is widespread in Haskell and is a key feature of its type system. Most polymorphism in Haskell falls into one of two broad categories: parametric polymorphism and ad-hoc polymorphism.
A function that can evaluate to or be applied to values of different types is known as a polymorphic function. A data type that can appear to be of a generalized type (e.g. a list with elements of arbitrary type) is designated polymorphic data type like the generalized type from which such specializations are made.
Universal or parametric polymorphism is another type of polymorphism. Unlike ad hoc, which is based on type, universal polymorphism is type-agnostic. Ad hoc polymorphism is derived from the loose translation of “ad hoc,” which is “for this.” That means the polymorphism relates specifically to certain data types.
Parametric Polymorphism opens a way to use the same piece of code for different types. It is implemented by the use of Templates. Using Templates, the same function can be parameterized with different types of data, but this needs to be decided at compile-time itself, and hence, this polymorphism is named so.
There are two flavors of polymorphism in Haskell:
The first is the most general -- a function is parametrically polymorphic if it behaves uniformly for all types, in at least one of its type parameters.
For example, the function length
is polymorphic -- it returns the length of a list, no matter what type is stored in its list.
length :: [a] -> Int
The polymorphism is indicated by a lower case type variable.
Now, if you have custom behavior that you want to have for a certain set of types, then you have bounded polymorphism (also known as "ad hoc"). In Haskell we use type classes for this.
The class declares which function will be available across a set of types:
class FunnyShow a where
funnyshow :: a -> String
and then you can define instances for each type you care about:
instance FunnyShow Int where
funnyshow i = show (i+1)
and maybe:
instance FunnyShow [Char] where
funnyshow s = show (show s)
Here is how you can achieve something similar using type families.
Well if you have same return types then you can achieve the behaviour without using type families and just using type classes alone as suggested by Don.
But it is better to use type families when you want to support more complex adhoc polymorphism, like different return types for each instance.
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE TypeFamilies #-}
class F a where
type Out a :: *
f :: a -> Out a
instance F String where
type Out String = String
f = show . length
instance F Int where
type Out Int = String
f = show
instance F Float where
type Out Float = Float
f = id
In ghci
*Main> f (2::Int)
"2"
*Main> f "Hello"
"5"
*Main> f (2::Float)
2.0
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