问题描述
syb , Data.Typeable 有几个泛型库有许多重叠模块, , Data.Data , GHC.Generics ),但是我遇到了一个非常基本的通用编程任务。我希望能够在相同形状的类型之间进行转换,即我想要在同构类型之间有一个多态的类型转换函数,本质上是在(PDF)where indexed type families被提及。
我不关心如何取消我的样板,而是能够围绕总和和产品抽象构建新库。
下面的问题是以 GHC.Generic 的形式出现的,我认为这与我所需要的最接近,但其他解决方案是值得欢迎的。
以下两种形状相同
<$ c $ (通用,显示)
数据Pair2 = Pair2 Char Int(派生)(通用,显示)
我想使用GHC.Generics在它们之间转换值。由于所有幻象参数和其他废话,以下情况未能检查:
f :: Pair - > Pair2
f = to。 from
最终,我想要一个类似于的函数fromInteger 对任何 Generic (或任何其他类可以支持的)实例都有一个多态返回值。我想我正在寻找像 GHC.Generics :
--class:
类型family NormalForm a
类ToGeneric a其中
to :: a - > NormalForm a
类FromGeneric b其中
来自:: NormalForm b - > b
--examples:
data A = A Char Int推导显示
数据B = B Char Int推导显示
类型实例NormalForm A = (Char,Int)
实例ToGeneric A其中
到(A ab)=(a,b)
实例FromGeneric A其中
来自(a,b)= A ab
类型实例NormalForm B =(Char,Int)
实例ToGeneric B其中
到(B ab)=(a,b)
实例FromGeneric B其中
from(a,b)= B ab
- 我正在寻找
的函数coerce ::(ToGeneric a,FromGeneric b,NormalForm a〜NormalForm b)=> ; a - > b
coerce = from。到
通过以上我们可以做我想做的一切:
* Main> (强迫$ A'a'1):: B
B'a'1
* Main> (强迫$ A'a'1):: A
A'a'1
编辑:这是Nathan Howell的 f 函数在实际情况下的工作原理。
问题
-
这可能与haskell平台中的库有关吗?如果不是,可以定义一个库,它利用现有的派生机制来执行 Generic , Data 等,而不诉诸于TH?
$如果相同形状意味着数据类型与构造函数名称相同,记录选择器和类型同义词,那么数据类型转换就如遍历一样简单
$ $ $ $ $ $ $ $ $ b $ {$#$ $ b $ {$#$ $ { - #LANGUAGE MultiParamTypeClasses# - }
{ - #LANGUAGE FlexibleContexts,FlexibleInstances# - }
导入GHC.Generics
conv
::( Generic a,Generic b,Conv(Rep a)(Rep b))
=> a - > b
conv = to。简历 。从
类Conv a b其中
cv :: a x - > b x
- 跳过不相关的部分:数据类型名称,构造函数名称,选择符
实例Conv f1 f2 => Conv(M1 i1 c1 f1)(M1 i2 c2 f2)其中
cv = M1。简历 。 unM1
实例(Conv a1 a2,Conv b1 b2)=> Conv(a1:*:b1)(a2:*:b2)其中
cv〜(a:*:b)= cv a:*:cv b
instance(Conv a1 a2 ,Conv b1 b2)=> Conv(a1:+:b1)(a2:+:b2)其中
cv(L1 a)= L1 $ cv a
cv(R1 b)= R1 $ cv b
- 复制值
实例Conv U1 U1其中cv = id
实例Conv(K1 R c)(K1 R c)其中cv = id
测试用例:
数据A = A1 String Int | A2(Int,Int)派生(Generic,Show)
数据B = B1 [Char] Int | B2 {xy ::(Int,Int)}派生(Generic,Show)
数据X = X Int Int派生(Generic,Show)
* Main> conv $ X 3 14 ::(Int,Int)
(3,14)
* Main> conv $ A1hello42 :: B
B1hello42
* Main> conv $ A2(13,42):: B
B2 {xy =(13,42)}
更新
还有一些实例允许进行更有趣的转化: $ c $> c $ _ $ M1 $ K1()
- *>实例Conv U1(M1 S s(K1 R())) conv(Nothing :: Maybe Int):: Either()Int
- Left()
Conv(M1 S s(K1 R()))U1其中
cv _ = U1
- *> conv(Left():: Either()Int):: Maybe Int
- Nothing
- 这个需要OverlappingInstances
实例(Generic c1,Generic c2,Conv (Rep c1)(Rep c2))
=> Conv(K1 R c1)(K1 R c2)
其中
cv(K1 x)= K1 $ conv x
- *> conv(Right Nothing :: Either()(Maybe Int)):: Maybe(Either()Int)
- Just(Left())
- data List a = Empty |缺点(列表a)派生(通用,显示)
- *> conv [1,2,3 :: Int] :: List Int
- Cons 1(Cons 2(Cons 3 Empty))
There are several generics libraries with numerous overlapping modules in just the Haskell Platform alone (syb, Data.Typeable, Data.Data, GHC.Generics), but I'm having trouble with a very basic generic programming task.
I want to be able to convert between types of the same shape, i.e. I want a polymorphic, typed conversion function between isomorphic types, essentially what is offered at the end of this paper(PDF) where indexed type families are mentioned.
I'm not concerned with scrapping my boilerplate, but rather with being able to build new libraries around sum and product abstractions.
The question below is in terms of GHC.Generic which I thought was closest to what I needed, but other solutions are welcome.
The following two types have the same shape
data Pair = Pair Char Int deriving (Generic, Show) data Pair2 = Pair2 Char Int deriving (Generic, Show)
I want to convert values between them using GHC.Generics. The following fails to typecheck because of all the phantom parameters and other nonsense:
f :: Pair -> Pair2 f = to . from
Ultimately I want a function akin to fromInteger that has a polymorphic return value for any Generic (or whatever other class could support this) instance. I guess I'm looking for something like GHC.Generics:
--class: type family NormalForm a class ToGeneric a where to :: a -> NormalForm a class FromGeneric b where from :: NormalForm b -> b --examples: data A = A Char Int deriving Show data B = B Char Int deriving Show type instance NormalForm A = (Char,Int) instance ToGeneric A where to (A a b) = (a,b) instance FromGeneric A where from (a,b) = A a b type instance NormalForm B = (Char,Int) instance ToGeneric B where to (B a b) = (a,b) instance FromGeneric B where from (a,b) = B a b -- the function I'm looking for coerce :: (ToGeneric a, FromGeneric b, NormalForm a ~ NormalForm b)=> a -> b coerce = from . to
With the above we can do everything I want:
*Main> (coerce $A 'a' 1) :: B B 'a' 1 *Main> (coerce $A 'a' 1) :: A A 'a' 1
EDIT: This is how Nathan Howell's f function seems to work below, actually.
Questions
Is this possible to do with libraries currently in the haskell platform?
If not, could a library be defined that leveraged the existing deriving mechanism for Generic, Data, etc. without resorting to TH?
If "of the same shape" means that datatypes are equal up to constructor names, record selectors and type synonyms then datatype conversion is as simple as traversing representation.
{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleContexts, FlexibleInstances #-} import GHC.Generics conv :: (Generic a, Generic b, Conv (Rep a) (Rep b)) => a -> b conv = to . cv . from class Conv a b where cv :: a x -> b x -- skip irrelevant parts: datatype name, constructor name, selector instance Conv f1 f2 => Conv (M1 i1 c1 f1) (M1 i2 c2 f2) where cv = M1 . cv . unM1 instance (Conv a1 a2, Conv b1 b2) => Conv (a1 :*: b1) (a2 :*: b2) where cv ~(a :*: b) = cv a :*: cv b instance (Conv a1 a2, Conv b1 b2) => Conv (a1 :+: b1) (a2 :+: b2) where cv (L1 a) = L1 $ cv a cv (R1 b) = R1 $ cv b -- copy values instance Conv U1 U1 where cv = id instance Conv (K1 R c) (K1 R c) where cv = id
Test case:
data A = A1 String Int | A2 (Int,Int) deriving (Generic, Show) data B = B1 [Char] Int | B2 { xy :: (Int,Int) } deriving (Generic, Show) data X = X Int Int deriving (Generic, Show) *Main> conv $ X 3 14 :: (Int,Int) (3,14) *Main> conv $ A1 "hello" 42 :: B B1 "hello" 42 *Main> conv $ A2 (13,42) :: B B2 {xy = (13,42)}
Update
A few more instances allow more interesting conversions:
instance Conv U1 (M1 S s (K1 R ())) where cv _ = M1 $ K1 () -- *> conv (Nothing :: Maybe Int) :: Either () Int -- Left () instance Conv (M1 S s (K1 R ())) U1 where cv _ = U1 -- *> conv (Left () :: Either () Int) :: Maybe Int -- Nothing -- this one requires OverlappingInstances instance (Generic c1, Generic c2, Conv (Rep c1) (Rep c2)) => Conv (K1 R c1) (K1 R c2) where cv (K1 x) = K1 $ conv x -- *> conv (Right Nothing :: Either () (Maybe Int)) :: Maybe (Either () Int) -- Just (Left ()) -- data List a = Empty | Cons a (List a) deriving (Generic, Show) -- *> conv [1,2,3::Int] :: List Int -- Cons 1 (Cons 2 (Cons 3 Empty))
这篇关于使用标准的haskell泛型库来实现类型化的type-isomorphisms的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!