将类型级别列表解构为嵌套元组而没有用于' []的哨兵

时间:2016-11-12 17:04:14

标签: haskell

我试图将类型列表(例如'[Int, Int])解构为类型和通过类型运算符创建该类型的方式(同构到嵌套元组但写得更好)。例如:

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeFamilies, FlexibleInstances #-}

import Data.Kind (Type)

data a :<> b = a :<> b
infixr 8 :<>

class Construct a where
  type Result a :: Type

instance forall a as. (Show a, Construct as) => Construct (a:as) where
  type Result (a:as) = a :<> (Result as)

instance Construct '[] where
  type Result '[] = ()

使用时,我得到了例如。

λ  :kind! Result '[Int, Int, Int]
Result '[Int, Int, Int] :: *
= Int :<> (Int :<> (Int :<> ()))

请注意我不想要的:<> ()。首先,我尝试更具体地匹配列表元素的长度,例如, &#39; [a,b]:

instance forall a b. (Show a, Show b) => Construct ('[a,b]) where
  type Result '[a,b] = a :<> b

但当然不起作用:

Conflicting family instance declarations:
  Result (a : as) = a :<> Result as -- Defined at test.hs:14:8
  Result '[a, b] = a :<> b -- Defined at test.hs:22:8

我总是可以构建N个特定实例:

instance forall a. (Show a) => Construct '[a] where
  type Result '[a] = a

instance forall a b. (Show a, Show b) => Construct '[a,b] where
  type Result '[a, b] = a :<> b

instance forall a b c. (Show a, Show b, Show c) => Construct '[a,b,c] where
  type Result '[a, b, c] = a :<> b :<> c

-- etc. up to N

但这似乎非常不满意。

有没有办法使用递归定义解包到Int :<> (Int :<> Int)而不是Int :<> (Int :<> (Int :<> ())))

1 个答案:

答案 0 :(得分:6)

使用封闭式家庭。它们从上到下匹配,所以没有冲突。

type family Construct (xs :: [Type]) :: Type where
  Construct '[x]      = x
  Construct (x ': xs) = x :<> Construct xs

现在Construct [Int, Int, Int]缩减为Int :<> (Int :<> Int)

但是,如果我对你打算如何使用它大致正确,那么使用异构列表会更好,因为它们更容易使用并且具有更好的类型推断。< / p>

{-# language
  UndecidableInstances, RankNTypes, TypeOperators, TypeFamilies,
  TypeApplications, ScopedTypeVariables, GADTs, DataKinds, PolyKinds,
  ConstraintKinds, AllowAmbiguousTypes #-}

import Data.List
import Data.Kind

data HList (ts :: [Type]) :: Type where
  Nil  :: HList '[]
  (:>) :: t -> HList ts -> HList (t ': ts)
infixr 5 :>

-- example
foo :: HList [Int, String, Bool]  
foo = 0 :> "foo" :> True :> Nil

作为HList上有用操作的示例,如果我们知道每个元素类型满足类约束,我们可以映射列表,将其收集到同类列表中或保留元素类型: / p>

type family AllC c (xs :: [a]) :: Constraint where
  AllC c '[]       = ()
  AllC c (x ': xs) = (c x, AllC c xs)

hmap :: forall c ts. AllC c ts => (forall x. c x => x -> x) -> HList ts -> HList ts
hmap f Nil       = Nil
hmap f (x :> xs) = f x :> hmap @c f xs

hmap' :: forall c ts r. AllC c ts => (forall x. c x => x -> r) -> HList ts -> [r]
hmap' f Nil       = []
hmap' f (x :> xs) = f x : hmap' @c f xs

我们可以使用TypeApplications来设置c约束。我们只需使用Show即可为HList实施hmap'个实例:

instance AllC Show ts => Show (HList ts) where
  show xs = "[" ++ intercalate ", " (hmap' @Show show xs) ++ "]"

现在我们有了ghci:

> foo
[0, "foo", True]

,因为foo的所有元素类型都有Show个实例。