受有关ADT之间多态函数的问题启发,我正在尝试创建多个(不止两个)类型之间的同构,以便每当我需要同构但不是相同类型时,我可以在我的代码中添加一些convert
。
假设我有3个ADT:
data AB = A | B deriving (Show)
data CD = C | D deriving (Show)
data EF = E | F deriving (Show)
使用lens
,我可以在AB和CD之间以及CD和EF之间实现2个同构:
{-# LANGUAGE MultiParamTypeClasses #-}
class Isomorphic a b where
convert :: Iso' a b
instance Isomorphic AB CD where
convert = iso ab2cd cd2ab
where ab2cd A = C
ab2cd B = D
cd2ab C = A
cd2ab D = B
instance Isomorphic AB EF where
convert = iso ab2ef ef2ab
where ab2ef A = E
ab2ef B = F
ef2ab E = A
ef2ab F = B
将A
转换为E
很容易:A^.convert :: EF
。将D
转换为B
也很容易:D^.from convert :: AB
。但如果我想通过A
从C
转换为E
,则必须为每个中间转换注释类型:
(C^.from convert :: AB)^.convert :: EF
我理解编译器为什么不能推导出中间类型。可能是因为有几个同构可以从 C
转换到 E
。但是,我能简化我的代码,以便不必在每个地方手动注释类型吗?
我可以写一个新实例,直接在 CD
和 EF
之间进行转换,但是如果我有超过3种类型怎么办?如果我有5个同构类型,我将不得不指定10个实例,因为同构对象之间的同构数是完全图中边的数量,这是一个三角形数。我宁愿指定 n-1
个实例,并权衡编写更多的 convert
或 from convert
。
是否有一种惯用的方法来使用来自 lens
的 Iso
在多个类型之间建立同构,以便尽量减少样板代码并且我不必在每个地方都加上类型注释?如果我必须使用 TemplateHaskell,我该如何做到这一点?
动机是因为在我的工作中,我有许多非常复杂但很蠢的类型,其中 () -> (() -> ()) -> X
和 ((), X)
同构于 X
。我必须手动包装和解包所有内容,我想要一种多态的方式将复杂的类型简化为更简单的同构类型。
Data.Coerce
对此并不是很有用。 - jberryman