使用镜头的3种以上类型的同构

时间:2016-09-09 18:11:06

标签: haskell type-conversion lens isomorphism

灵感来自a question on polymorphic function between ADTs我试图在多个(不仅仅是2个)类型之间创建同构,因此每次我需要一个同构但不相同的类型时,我可以用一些{{1 }}

假设我有3个ADT:

convert

使用lens我可以在AB和CD,CD和EF之间实现2个同构:

data AB = A | B deriving (Show)
data CD = C | D deriving (Show)
data EF = E | F deriving (Show)

{-# 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 :: ABC转换为E,我必须为每个中间转换注释类型:

A

我理解为什么编译器无法推断出中间类型。可能有几个同构,人们可以通过这些同构从(C^.from convert :: AB)^.convert :: EF C。但是我可以简化我的代码,所以我不会在任何地方手动注释类型吗?

我可以写另一个实例直接在ECD之间进行转换,但如果我有超过3种类型呢?如果我有5个同构类型,我将必须指定10个实例,因为同构对象之间的isos数是完整图形中的一些边,即triangular number。我宁愿指定EF个实例,我需要权衡更多n-1convert

是否存在使用from convert中的Iso在多个类型之间建立同构的惯用方法,以便最少量的样板文件并且我不需要对所有内容进行类型注释?如果我必须使用TemplateHaskell,我该怎么做?

动机是,在我的工作中,我有许多荒谬复杂但又愚蠢的类型,其中lens() -> (() -> ()) -> X((), X)同构。我必须手动包装和解包所有内容,我希望采用多态方式将复杂类型简化为更简单的同构类型。

1 个答案:

答案 0 :(得分:11)

你可以将你的同构构建为一个星图:拥有一个规范的" hub"所有其他人连接的类型。缺点是您必须在每个实例中明确指定集线器,并且您只能在共享集线器的类型之间进行转换。但是,您的两个要求(良好的类型推断和线性数量的实例)将得到满足。以下是您将如何做到这一点:

{-# LANGUAGE TypeFamilies #-}
import Control.Lens
import Unsafe.Coerce

data AB = A | B deriving (Show)
data CD = C | D deriving (Show)
data EF = E | F deriving (Show)

class Isomorphic a where
    type Hub a
    convert :: Iso' a (Hub a)

viaHub :: (Isomorphic a, Isomorphic b, Hub a ~ Hub b) => a -> b
viaHub x = x ^. convert . from convert

instance Isomorphic AB where
    type Hub AB = AB
    convert = id

instance Isomorphic CD where
    type Hub CD = AB
    convert = unsafeCoerce -- because I'm too lazy to do it right

instance Isomorphic EF where
    type Hub EF = AB
    convert = unsafeCoerce

在ghci:

> viaHub A :: EF
E
> viaHub A :: CD
C
> viaHub E :: AB
A
> viaHub E :: CD
C

以下是您如何将此用于示例:

class Unit a where unit :: a
instance Unit () where unit = ()
instance Unit b => Unit (a -> b) where unit _ = unit

instance Isomorphic X where
    type Hub X = X
    convert = id

instance (Unit a, Isomorphic b) => Isomorphic (a -> b) where
    type Hub (a -> b) = Hub b
    convert = iso ($unit) const . convert

instance Isomorphic a => Isomorphic ((), a) where
    type Hub ((), a) = Hub a
    convert = iso snd ((,)()) . convert

instance Isomorphic a => Isomorphic (a, ()) where
    type Hub (a, ()) = Hub a
    convert = iso fst (flip(,)()) . convert

现在你将拥有,例如<​​/ p>

viaHub :: (() -> (() -> ()) -> X) -> ((), X)