我试图将类型列表(例如'[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 :<> ())))
?
答案 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
个实例。