在ad-hoc多态函数和参数多态函数之间进行转换的好方法

时间:2016-07-12 10:35:05

标签: haskell reflection parametric-polymorphism adhoc-polymorphism

我想知道是否有一般方法可以在ad-hoc多态函数和参数多态函数之间进行转换。换句话说,给定一个ad-hoc多态函数,如何实现其参数对应?反过来呢?

sort为例。根据{{​​1}}:

来编写sort :: Ord a => [a] -> [a]很容易
sortBy

但反过来似乎很棘手,到目前为止,我能做的最好的事情就是去“面向对象”:

sort :: Ord a => [a] -> [a]
sort = sortBy compare

但这听起来更像是一个黑客而不是正确的解决方案。

所以我想知道:

  1. 这个具体例子有更好的方法吗?
  2. 在ad-hoc多态函数和参数多态函数之间进行转换的一般技术是什么?

2 个答案:

答案 0 :(得分:8)

我不一定说你应该这样做,但你可以使用reflection传递比较功能而无需将其与列表中的每个元素打包在一起:

{-# 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

鉴于( heh )上述基础架构,您可以按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包装器的所有潜在运行时成本

答案 1 :(得分:5)

Cactus的回答依赖于Expander中有点阴暗的Given类。但是,没有它可以使用反射。

reflection

检查生成的Core表明这会编译为{-# 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的{​​{1}}类看起来更薄,但Ed Kmett并不喜欢生成的API。