3型和更多类型之间的同构使用镜头 [英] Isomorphisms between 3 and more types using lens

查看:128
本文介绍了3型和更多类型之间的同构使用镜头的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

ADT之间多态功能的问题启发我试图在多个(不仅仅是2个)之间创建同构,类型,所以每次我需要一个同构而不是相同的类型时,我可以用一些 convert转换



假设我有3个ADT:

  data AB = A | B导出(显示)
数据CD = C | D导出(显示)
数据EF = E | F导出(显示)

使用 $ b
$ b

  { - #LANGUAGE MultiParamTypeClasses# - } 
class同构ab其中
转换:: Iso'ab

instance Isomorphic AB CD其中
convert = iso ab2cd cd2ab
其中ab2cd A = C
ab2cd B = D
cd2ab C = A
cd2ab D = B

实例同构AB EF其中
convert = iso ab2ef ef2ab
其中ab2ef A = E
ab2ef B = F
ef2ab E = A
ef2ab F = B

A 转换为 E 很简单: A ^ .convert :: EF 。将 D 转换为 B 也很容易: D ^ .from convert :: AB code>。但如果我想通过 A C 转换为 E >,我必须注释每种中间转换的类型:

 (C ^ .from convert :: AB)^。convert: :EF 

我理解编译器为什么不能推断中间类型。可能有几种同态现象可以从 C E 中获得。但我可以简化我的代码,所以我不手动注释到处类型?

我可以写另一个实例直接转换 CD EF ,但如果我有超过3种类型?如果我有5个同构类型,我将不得不指定10个实例,因为同构对象之间的isos数量是完整图形中的一些边,这是一个三角数字。我更愿意指定 n-1 实例,并进行折衷,我写更多 convert 转换



是否有一种惯用的方法可以使用 Iso from lens ,这样样板数量最少,我不必键入所有内容?如果我必须使用TemplateHaskell,那么我该怎么做?



动机是,在我的工作中,我有许多荒谬复杂但愚蠢的类型,其中() - > (() - >()) - > X ((),X)同构于 X 。我不得不手动包装和解包所有东西,我希望有一些多态的方法来将复杂类型简化为更简单的同构类型。

你可以把你的同构构造成一个星形图:有一个规范的集线器类型,所有其他的连接。缺点是您必须在每个实例中明确指定中心,并且只能在共享中心的类型之间进行转换。然而,你的两个要求(好的类型推断和线性数量的实例)将会被满足。你可以这样做:

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

数据AB = A | B导出(显示)
数据CD = C | D导出(显示)
数据EF = E | F导出(显示)

类同构a其中
类型Hub a
convert :: Iso'a(Hub a)

viaHub :: :(同构a,同构b,Hub a〜Hub b)=> a - > b
viaHub x = x ^。兑换 。从转换

实例同构AB其中
类型Hub AB = AB
convert = id

实例同构CD其中
类型Hub CD = AB
convert = unsafeCoerce - 因为我懒得做正确的

实例同形EF其中
类型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 =>单元(a - > b)单元_ =单元

实例同构X其中
类型Hub X = X
convert = id

实例(单元a,同构b)=>同构(a - &b; b)其中
类型Hub(a - > b)= Hub b
convert = iso($ unit)const。转换

实例同构a =>同构((),a)其中
类型Hub((),a)= Hub a
convert = iso snd((,)())。转换

实例同构a =>同构(a,())其中
类型Hub(a,())= Hub a
convert = iso fst(flip(,)())。转换

现在您将获得,例如

<$ (() - >()) - > X) - >(& ((),X)


Inspired by a question on polymorphic function between ADTs I'm trying to create isomorphisms between multiple (not just 2) types, so that every time I need an isomorphic but not the same type, I can sprinkle my code with some convert.

Suppose I have 3 ADTs:

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

Using lens I can implement 2 isomorphisms between AB and CD, and CD and EF:

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

Converting A to E is easy: A^.convert :: EF. Converting D to B is also easy: D^.from convert :: AB. But if I want to convert from C to E via A, I have to annotate types for every intermediary conversion:

(C^.from convert :: AB)^.convert :: EF

I understand why compiler can't infer intermediate types. It could be that there are several isomorphisms through which one could get from C to E. But can I simplify my code so I don't manually annotate types everywhere?

I could just write yet another instance to convert directly between CD and EF, but what if I have more than 3 types? If I had 5 isomorphic types, I would have to specify 10 instances, because the number of isos between isomorphic objects is a number of edges in a complete graph, which is a triangular number. I'd rather specify n-1 instances, with a trade-off that I write more convert or from convert.

Is there an idiomatic way to establish isomorphisms between multiple types using Iso from lens so that there is least amount of boilerplate and I don't have to type-annotate everything? If I have to use TemplateHaskell for that, how do I do that?

The motivation is that in my work I have many ridiculously complicated but stupid types, where () -> (() -> ()) -> X and ((), X) are isomorphic to X. I have to manually wrap and unwrap everything and I would like some polymorphic way to reduce the complicated types to simpler isomorphic types.

解决方案

You can structure your isomorphisms as a star graph: have a canonical "hub" type to which all others connect. The downside is that you will have to specify the hub explicitly in each instance, and you will only be able to convert between types which share a hub. However your two requirements (good type inference, and a linear number of instances) will be met. Here's how you would do that:

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

In ghci:

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

Here's how you might use this for your examples:

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

Now you will have, e.g.

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

这篇关于3型和更多类型之间的同构使用镜头的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆