Haskell 将类型级别列表解构为嵌套元组,而不使用';[]

Haskell 将类型级别列表解构为嵌套元组,而不使用';[],haskell,Haskell,我试图将类型列表(例如,'[Int,Int])分解为一个类型,并通过类型运算符(与嵌套元组同构,但编写起来更方便)创建该类型。例如: {-#语言范围的TypeVariables} {-#语言类型#-} {-#语言类型族,FlexibleInstances} 导入数据。种类(类型) 数据a:b=a:b infixr 8: 类构造一个where 类型结果a::类型 所有as的实例。(显示a,构造为)=>构造(a:as),其中 类型结果(a:as)=a:(结果为) 实例构造“[]其中 键入结果“[]=

我试图将类型列表(例如,
'[Int,Int]
)分解为一个类型,并通过类型运算符(与嵌套元组同构,但编写起来更方便)创建该类型。例如:

{-#语言范围的TypeVariables}
{-#语言类型#-}
{-#语言类型族,FlexibleInstances}
导入数据。种类(类型)
数据a:b=a:b
infixr 8:
类构造一个where
类型结果a::类型
所有as的实例。(显示a,构造为)=>构造(a:as),其中
类型结果(a:as)=a:(结果为)
实例构造“[]其中
键入结果“[]=()
当我使用这个时,我得到例如

λ  :kind! Result '[Int, Int, Int]
Result '[Int, Int, Int] :: *
= Int :<> (Int :<> (Int :<> ()))
λ:善良!结果'[Int,Int,Int]
结果“[Int,Int,Int]::*
=Int:(Int:())
注意结尾的
:()
,这是我不想要的。起初,我尝试更具体地匹配列表元素的长度,例如,“[a,b]:

所有a b的实例。(显示a,显示b)=>构造('[a,b]),其中
键入结果“[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
冲突的族实例声明:
结果(a:as)=a:Result as--在测试时定义。hs:14:8
结果'[a,b]=a:b——在测试时定义。hs:22:8
我始终可以构造多达N个特定实例:

所有a的实例。(显示a)=>构造“[a]其中
键入结果“[a]=a
所有a和b的实例。(显示a,显示b)=>构造“[a,b]其中
键入结果“[a,b]=a:b
所有ABC的实例。(Show a,Show b,Show c)=>构造'[a,b,c],其中
键入结果“[a,b,c]=a:b:c
--等高达N
但这似乎很不令人满意


有没有一种方法可以使用递归定义解包到
Int:(Int:Int)
而不是
Int:(Int:())
。它们自上而下匹配,所以没有冲突

type family Construct (xs :: [Type]) :: Type where
  Construct '[x]      = x
  Construct (x ': xs) = x :<> Construct xs
作为对
HList
的一个有用操作的示例,如果我们知道每个元素类型都满足一个类约束,我们可以映射列表,或者将其收集到一个同构列表中,或者保留元素类型:

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
约束。只需使用
hmap'
,我们就可以为
HList
实现
Show
实例:

instance AllC Show ts => Show (HList ts) where
  show xs = "[" ++ intercalate ", " (hmap' @Show show xs) ++ "]"
现在我们在ghci有:

> foo
[0, "foo", True]

这是因为
foo
的所有元素类型都有
Show
实例。

谢谢!您知道类型类的等效类型族吗?也就是说,如果我现在想写一个匹配的函数,比如
handler::Result a->Text
,我似乎也有同样的解包哨兵问题,只是转到了类型类。@关于更好的解决方案,请参见编辑(我想你会怎么做)。
> foo
[0, "foo", True]