是否可以重新组织嵌套元组?

时间:2013-04-17 05:14:49

标签: haskell

我想做的是这样的事情:

采用任意多态元组:

x = (((1, ""), Nothing), ('', 6))

用这种类型的东西重新组织(不一定是相同的顺序,但结构相同。:

(Int, (Char, (Maybe Int, (String, (Int, ()))))

我真的不知道这个模式的名称,所以我无法尽我所能使用谷歌。

2 个答案:

答案 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 u2Just 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))

和两个函数flattenVflattenU分别用于展平元组值及其类型的编码:

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, ())))))

(显然,您希望以正确的顺序获取类型,并确保所有类型都编译。)