我想做的是这样的事情:
采用任意多态元组:
x = (((1, ""), Nothing), ('', 6))
用这种类型的东西重新组织(不一定是相同的顺序,但结构相同。:
(Int, (Char, (Maybe Int, (String, (Int, ()))))
我真的不知道这个模式的名称,所以我无法尽我所能使用谷歌。
答案 0 :(得分:12)
如果您只需处理此特定情况,即从
转换(((Int, String), Maybe Int), (Char, Int))
到
(Int, (Char, (Maybe Int, (String, (Int, ()))))
然后,根据您是要保留Int
- 组件的顺序还是交换它们,您只需使用以下两个功能之一:
from1 (((m, s), mb), (c, n)) = (m, (c, mb, (s, (n, ()))))
from2 (((m, s), mb), (c, n)) = (n, (c, mb, (s, (m, ()))))
但我们当然可以更加雄心勃勃,并寻求更通用的解决方案;例如,请参阅Jeuring and Atanassow (MPC 2004)。为此,让我们启用一些语言扩展
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
并为我们可用于表示元组类型的代码引入GADT
infixr 5 :*:
data U a where
Unit :: U ()
Int :: U Int
Char :: U Char
List :: U a -> U [a]
Maybe :: U a -> U (Maybe a)
(:*:) :: U a -> U b -> U (a, b)
例如,您的示例中的目标类型现在可以通过表达式
进行编码Int :*: Char :*: Maybe Int :*: string :*: Int :*: Unit
类型
U (Int, (Char, (Maybe Int, (String, (Int, ()))))
为方便起见,我们介绍
string :: U String
string = List Char
我们还引入了一种显式类型的元组值
data Typed where
Typed :: U a -> a -> Typed
和类型级别相等的概念:
infix 4 :==:
data a :==: b where
Refl :: a :==: a
有了这个,我们可以定义元组类型编码的异构等式检查:
eq :: U a -> U b -> Maybe (a :==: b)
eq Unit Unit = Just Refl
eq Int Int = Just Refl
eq Char Char = Just Refl
eq (List u1) (List u2) = case eq u1 u2 of
Just Refl -> Just Refl
_ -> Nothing
eq (Maybe u1) (Maybe u2) = case eq u1 u2 of
Just Refl -> Just Refl
_ -> Nothing
eq (u11 :*: u12) (u21 :*: u22) = case (eq u11 u21, eq u12 u22) of
(Just Refl, Just Refl) -> Just Refl
_ -> Nothing
eq _ _ = Nothing
如果eq u1 u2
和Just Refl
编码相同的元组类型,那么u1
会返回u2
,否则会Nothing
。在Just
- 情况下,构造函数Refl
充当类型检查器的证明,即元组类型确实相同。
现在我们希望能够将元组类型转换为“扁平化”,即右嵌套表示。为此,我们引入了一个类型族Flatten
:
type family Flatten a
type instance Flatten () = ()
type instance Flatten Int = Flatten (Int, ())
type instance Flatten Char = Flatten (Char, ())
type instance Flatten [a] = Flatten ([a], ())
type instance Flatten (Maybe a) = Flatten (Maybe a, ())
type instance Flatten ((), a) = Flatten a
type instance Flatten (Int, a) = (Int, Flatten a)
type instance Flatten (Char, a) = (Char, Flatten a)
type instance Flatten ([a], b) = ([a], Flatten b)
type instance Flatten (Maybe a, b) = (Maybe a, Flatten b)
type instance Flatten ((a, b), c) = Flatten (a, (b, c))
和两个函数flattenV
和flattenU
分别用于展平元组值及其类型的编码:
flattenV :: U a -> a -> Flatten a
flattenV Unit _ = ()
flattenV Int n = flattenV (Int :*: Unit) (n, ())
flattenV Char c = flattenV (Char :*: Unit) (c, ())
flattenV (List u) xs = flattenV (List u :*: Unit) (xs, ())
flattenV (Maybe u) mb = flattenV (Maybe u :*: Unit) (mb, ())
flattenV (Unit :*: u) (_, x) = flattenV u x
flattenV (Int :*: u) (n, x) = (n, flattenV u x)
flattenV (Char :*: u) (c, x) = (c, flattenV u x)
flattenV (List _ :*: u) (xs, x) = (xs, flattenV u x)
flattenV (Maybe _ :*: u) (mb, x) = (mb, flattenV u x)
flattenV ((u1 :*: u2) :*: u3) ((x1, x2), x3)
= flattenV (u1 :*: u2 :*: u3) (x1, (x2, x3))
flattenU :: U a -> U (Flatten a)
flattenU Unit = Unit
flattenU Int = Int :*: Unit
flattenU Char = Char :*: Unit
flattenU (List u) = List u :*: Unit
flattenU (Maybe u) = Maybe u :*: Unit
flattenU (Unit :*: u) = flattenU u
flattenU (Int :*: u) = Int :*: flattenU u
flattenU (Char :*: u) = Char :*: flattenU u
flattenU (List u1 :*: u2) = List u1 :*: flattenU u2
flattenU (Maybe u1 :*: u2) = Maybe u1 :*: flattenU u2
flattenU ((u1 :*: u2) :*: u3) = flattenU (u1 :*: u2 :*: u3)
然后将两者合并为一个函数flatten
:
flatten :: U a -> a -> Typed
flatten u x = Typed (flattenU u) (flattenV u x)
我们还需要一种方法来从展平的表示中恢复元组件的原始嵌套:
reify :: U a -> Flatten a -> a
reify Unit _ = ()
reify Int (n, _) = n
reify Char (c, _) = c
reify (List u) (xs, _) = xs
reify (Maybe u) (mb, _) = mb
reify (Unit :*: u) y = ((), reify u y)
reify (Int :*: u) (n, y) = (n, reify u y)
reify (Char :*: u) (c, y) = (c, reify u y)
reify (List _ :*: u) (xs, y) = (xs, reify u y)
reify (Maybe _ :*: u) (mb, y) = (mb, reify u y)
reify ((u1 :*: u2) :*: u3) y = let (x1, (x2, x3)) = reify (u1 :*: u2 :*: u3) y
in ((x1, x2), x3)
现在,给定元组组件的类型代码u
和扁平元组以及其类型的编码,我们定义函数select
,它返回从元组中选择组件的所有可能方法使用与u
匹配的类型以及其余组件的展平表示:
select :: U b -> Typed -> [(b, Typed)]
select _ (Typed Unit _) = []
select u2 (Typed (u11 :*: u12) (x1, x2)) =
case u11 `eq` u2 of
Just Refl -> (x1, Typed u12 x2) : zs
_ -> zs
where
zs = [(y, Typed (u11 :*: u') (x1, x')) |
(y, Typed u' x') <- select u2 (Typed u12 x2)]
最后,我们可以定义一个函数conv
,它接受两个元组类型的代码和一个与第一个代码匹配的类型的元组,并将所有可能的转换返回到与第二个匹配的类型的元组中代码:
conv :: U a -> U b -> a -> [b]
conv u1 u2 x = [reify u2 y | y <- go (flattenU u2) (flatten u1 x)]
where
go :: U b -> Typed -> [b]
go Unit (Typed Unit _ ) = [()]
go (u1 :*: u2) t =
[(y1, y2) | (y1, t') <- select u1 t, y2 <- go u2 t']
作为一个例子,我们有
conv (Int :*: Char) (Char :*: Int) (2, 'x')
产量
[('x', 2)]
返回原始示例,如果我们定义
from = conv u1 u2
where
u1 = ((Int :*: string) :*: Maybe Int) :*: Char :*: Int
u2 = Int :*: Char :*: Maybe Int :*: string :*: Int :*: Unit
然后
from (((1, ""), Nothing), (' ', 6))
产量
[ (1, (' ', (Nothing, ("", (6, ())))))
, (6, (' ', (Nothing, ("", (1, ())))))
]
通过为可表示的元组类型引入类型类,我们可以使事情变得更好:
class Rep a where
rep :: U a
instance Rep () where rep = Unit
instance Rep Int where rep = Int
instance Rep Char where rep = Char
instance Rep a => Rep [a] where rep = List rep
instance Rep a => Rep (Maybe a) where rep = Maybe rep
instance (Rep a, Rep b) => Rep (a, b) where rep = rep :*: rep
这样,我们可以定义一个不需要元组类型代码的转换函数:
conv' :: (Rep a, Rep b) => a -> [b]
conv' = conv rep rep
然后,例如
conv' ("foo", 'x') :: [((Char, ()), String)]
产量
[(('x', ()), "foo")]
答案 1 :(得分:1)
我还在Haskell的近期,但我会用模式匹配函数来做这件事。
converter :: (((Int, String), Maybe a), (Char, Int)) -> (Int, (Char, Maybe Int, (String, (Int, ()))))
converter (((i1, s), m), (c, i2)) = (i1, (c, (m, (s, (i2, ())))))
你当然可以用类型变量替换所有具体类型,它也可以工作。
converter :: (((a, b), c), (d, e)) -> (a, (d, c, (b, (e, ()))))
converter (((i1, s), m), (c, i2)) = (i1, (c, (m, (s, (i2, ())))))
(显然,您希望以正确的顺序获取类型,并确保所有类型都编译。)