I'm wondering if there are general ways to convert between ad-hoc polymorphic functions and parametric polymorphic ones. In other words, given an ad-hoc polymorphic function, how to implement its parametric counterpart? and what about the other way around?
take sort
as an example. it's easy to write sort :: Ord a => [a] -> [a]
in terms of sortBy
:
sort :: Ord a => [a] -> [a]
sort = sortBy compare
but the other way around seems tricky, so far the best I can do is to go a bit "object-oriented":
import qualified Data.List as L
data OrdVal a = OV (a -> a -> Ordering) a
instance Eq (OrdVal a) where
(OV cmp a) == (OV _ b) = a `cmp` b == EQ
instance Ord (OrdVal a) where
(OV cmp a) `compare` (OV _ b) = a `cmp` b
sortBy :: (a -> a -> Ordering) -> [a] -> [a]
sortBy cmp = map unOV . L.sort . map (OV cmp)
where
unOV (OV _ v) = v
But this sounds more like a hack than proper solution.
so I'd like to know:
For ad hoc polymorphism, functions with the same name act differently for different types. Python is dynamically typed (it does not require the type to be specified). The Python addition function is an ad hoc polymorphism because it is a single function of the same name, “+”, that works on multiple types.
Ad-hoc Polymorphism allows functions having same name to act differently for different types.
Parametric polymorphism is a programming language technique that enables the generic definition of functions and types, without a great deal of concern for type-based errors. It allows language to be more expressive while writing generic code that applies to various types of data.
In programming languages, ad hoc polymorphism is a kind of polymorphism in which polymorphic functions can be applied to arguments of different types, because a polymorphic function can denote a number of distinct and potentially heterogeneous implementations depending on the type of argument(s) to which it is applied.
I'm not necessarily saying you should do this, but you can use reflection to pass around the comparison function without having to package it up with each element of the list:
{-# LANGUAGE UndecidableInstances #-}
import Data.Reflection
newtype O a = O a
instance Given (a -> a -> Ordering) => Eq (O a) where
x == y = compare x y == EQ
instance Given (a -> a -> Ordering) => Ord (O a) where
compare (O x) (O y) = given x y
Given (heh) the above infrastructure, you can then write sortBy
in terms of sort
as follows:
import Data.Coerce
import Data.List (sort)
sortBy :: (a -> a -> Ordering) -> [a] -> [a]
sortBy cmp = give cmp $ from . sort . to
where
to :: [a] -> [O a]
to = coerce
from :: [O a] -> [a]
from = coerce
(note that by using Data.Coerce
, we avoid all potential runtime cost of the O
wrapper)
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