Isomorphisms between 3 and more types using lens

自闭症网瘾萝莉.ら 提交于 2019-12-05 01:28:26
Daniel Wagner

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)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!