Хороший способ для преобразования между специальными полиморфными функциями и параметрическими полиморфными
Мне интересно, есть ли общие способы преобразования между специальными полиморфными функциями и параметрическими полиморфными. Другими словами, учитывая специальную полиморфную функцию, как реализовать ее параметрический аналог? а как же наоборот?
принимать sort
В качестве примера. это легко написать sort :: Ord a => [a] -> [a]
с точки зрения sortBy
:
sort :: Ord a => [a] -> [a]
sort = sortBy compare
но, наоборот, кажется хитрым, пока лучшее, что я могу сделать, это немного "объектно-ориентированный":
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
Но это больше похоже на взлом, чем на правильное решение.
поэтому я хотел бы знать:
- Есть ли лучшие способы для этого конкретного примера?
- Каковы общие методы преобразования между специальными полиморфными функциями и параметрическими?
2 ответа
Я не обязательно говорю, что вы должны сделать это, но вы можете использовать отражение, чтобы обойти функцию сравнения без необходимости упаковывать ее с каждым элементом списка:
{-# 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
Учитывая (хех) вышеупомянутую инфраструктуру, вы можете написать sortBy
с точки зрения sort
следующее:
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
(обратите внимание, что с помощью Data.Coerce
Мы избегаем всех потенциальных затрат времени выполнения O
обертка)
Ответ Кактуса опирается на несколько тенистый Given
класс в reflection
, Однако можно использовать отражение без этого.
{-# LANGUAGE ScopedTypeVariables, MultiParamTypeClasses, UndecidableInstances #-}
module SortReflection where
import Data.Reflection
import Data.List (sort)
import Data.Proxy
import Data.Coerce
newtype O s a = O {getO :: a}
instance Reifies s (a -> a -> Ordering) => Eq (O s a) where
a == b = compare a b == EQ
instance Reifies s (a -> a -> Ordering) => Ord (O s a) where
compare = coerce (reflect (Proxy :: Proxy s))
sortBy :: forall a . (a -> a -> Ordering) -> [a] -> [a]
sortBy cmp = reify cmp $
\(_ :: Proxy s) -> coerce (sort :: [O s a] -> [O s a])
Изучение произведенного ядра указывает на то, что это компилируется в тонкую оболочку вокруг sortBy
, Это выглядит еще тоньше, используя Reifies
класс на основе Tagged
вместо Proxy
, но Эд Кметт не любит API, который производит.