受到关于 ADT 之间多态函数的问题的启发,我试图在多个(不仅仅是 2 个)类型之间创建同构,这样每次我需要一个同构但不相同的类型时,我都可以在我的代码中撒上一些convert
.
假设我有 3 个 ADT:
data AB = A | B deriving (Show)
data CD = C | D deriving (Show)
data EF = E | F deriving (Show)
使用lens
I 可以实现 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
. 但是如果我想将 from 转换C
为E
via A
,我必须为每个中间转换注释类型:
(C^.from convert :: AB)^.convert :: EF
我理解为什么编译器不能推断中间类型。可能有几个同构可以通过它们从C
到E
。但是我可以简化我的代码,这样我就不用在任何地方手动注释类型了吗?
我可以编写另一个实例来直接在CD
and之间进行转换EF
,但是如果我有 3 种以上的类型怎么办?如果我有 5 个同构类型,我将不得不指定 10 个实例,因为同构对象之间的 iso 数量是完整图中的边数,它是一个三角形数。我宁愿指定n-1
实例,并权衡我写更多convert
或from convert
.
Iso
有没有一种惯用的方法来使用from建立多种类型之间的同构,lens
这样样板文件的数量最少,而且我不必对所有内容进行类型注释?如果我必须为此使用 TemplateHaskell,我该怎么做?
动机是,在我的工作中,我有许多非常复杂但愚蠢的类型,其中() -> (() -> ()) -> X
和((), X)
同构于X
. 我必须手动包装和展开所有内容,并且我想要一些多态方法来将复杂类型减少为更简单的同构类型。