动态覆写类型类方法:如何用T类型自定义相等函数调用需Eq约束的预定义函数?
解决方案
核心思路是通过newtype包装原类型,为包装类型自定义Eq实例(使用T中携带的相等函数),这样就能直接调用需要Eq约束的预定义函数,无需重写。
以实现elemIndices'为例:
import Data.List (elemIndices) data T a = T (a -> a -> Bool) [a] elemIndices' :: a -> T a -> [Int] elemIndices' x (T eq xs) = elemIndices (Wrap x) (map Wrap xs) where -- 局部定义包装类型 newtype Wrap b = Wrap b -- 为包装类型实现Eq实例,复用传入的eq函数 instance Eq (Wrap b) where Wrap a == Wrap b = eq a b
原理说明
Wrap作为newtype是编译期语法糖,运行时不会带来额外开销,仅用于为原类型附加自定义Eq逻辑。- 在
where块中为Wrap b定义的Eq实例,会把==操作完全映射到传入的eq函数上——这样预定义的elemIndices会自动使用我们指定的相等规则,而非原类型的默认Eq实例。 - 把目标元素
x和列表xs都包装成Wrap类型后调用elemIndices,由于列表长度、元素位置完全不变,返回的索引结果直接对应原列表。
测试验证
- 传入
elemIndices' 1 (T (==) [0,1,2,1,3]),结果为[1,3],和Data.List.elemIndices默认行为一致。 - 传入
elemIndices' 1 (T (/=) [0,1,2,1,3]),结果为[0,2,4],符合自定义相等逻辑的预期。
如果追求更高效率,可利用coerce替代map(newtype的零开销特性):
import Data.List (elemIndices) import Data.Coerce (coerce) data T a = T (a -> a -> Bool) [a] elemIndices' :: a -> T a -> [Int] elemIndices' x (T eq xs) = coerce (elemIndices (Wrap x) (coerce xs :: [Wrap a])) where newtype Wrap b = Wrap b instance Eq (Wrap b) where Wrap a == Wrap b = eq a b
内容的提问来源于stack exchange,提问作者mingmingrr
相关产品推荐
相关产品推荐

