异构列表是为ghc 7.6的新依赖类型工具提供的示例之一:
data HList :: [*] -> * where
HNil :: HList '[]
HCons:: a -> HList t -> HList (a ': t)
示例列表“li”编译正常:
li = HCons "Int: " (HCons 234 (HCons "Integer: " (HCons 129877645 HNil)))
显然我们希望HList在Show类中,但我只能提出以下使用相互递归约束(超类)的工作类实例化:
instance Show (HList '[]) where
show HNil = "[]"
instance (Show a, Show' (HList t)) => Show (HList (a ': t)) where
show l = "[" ++ show' l ++ "]"
class Show' a where
show' :: a -> String
instance Show' (HList '[]) where
show' HNil = ""
instance (Show a, Show' (HList t)) => Show' (HList (a ': t)) where
show' (HCons h l) = case l of
HNil -> show h
HCons _ _ -> show h ++ ", " ++ (show' l)
代码编译正常,li正确显示。所需的编译标志是:
{-# LANGUAGE DataKinds, TypeOperators, KindSignatures,
FlexibleContexts, GADTs, FlexibleInstances #-}
我尝试了以下更多直接定义的许多变体,但如果没有我能够理解ghc错误消息,它就无法编译:
instance Show (HList '[]) where
show HNil = "[]"
instance (Show a, Show (HList t)) => Show (HList (a ': t)) where
show l = "[" ++ (show' l) ++ "]" where
show' (HCons h s) = case s of
HNil -> show h
HCons _ _ -> show h ++ ", " ++ (show' s)
一些Haskell / ghc专家可能会理解为什么这不起作用,我很乐意听到原因。
谢谢
汉斯彼得
谢谢你,哈马尔,你的两个很好的工作例子,改进了我的第一个例子。
但我仍然不明白为什么我的第二个例子不起作用。你说“...... show”只知道如何显示当前的元素类型,而不是剩下的元素类型。“但是这个评论不会也适用于以下(工作)代码:
instance Show (HList '[]) where show HNil = ""
instance (Show a, Show (HList t)) => Show (HList (a ': t)) where
show (HCons h t) = case t of
HNil -> show h
HCons _ _ -> show h ++ ", " ++ (show t)
答案 0 :(得分:5)
正如内森在评论中所说,show'
只知道如何显示当前元素类型而不是剩余的元素类型。
与第一个示例一样,我们可以通过为show'
创建一个新类型来解决这个问题,尽管您只能使用一个Show
实例:
-- Specializing show' to HLists avoids needing a Show' (HList ts) constraint
-- here, which would require UndecidableInstances.
instance (Show' ts) => Show (HList ts) where
show xs = "[" ++ show' xs ++ "]"
class Show' ts where
show' :: HList ts -> String
instance Show' '[] where
show' HNil = ""
instance (Show a, Show' ts) => Show' (a ': ts) where
show' (HCons a s) = case s of
HNil -> show a
HCons {} -> show a ++ ", " ++ show' s
将所有必要的Show
约束带入show'
的另一种更为hackish的方法是使用ConstraintKinds
直接构建所有必要约束的列表。
-- In addition to the extensions in the original code:
{-# LANGUAGE TypeFamilies, ConstraintKinds, UndecidableInstances #-}
import GHC.Exts
-- ShowTypes [a, b, c, ...] = (Show a, Show b, Show c, ...)
type family ShowTypes (a :: [*]) :: Constraint
type instance ShowTypes '[] = ()
type instance ShowTypes (a ': t) = (Show a, ShowTypes t)
instance ShowTypes ts => Show (HList ts) where
show xs = "[" ++ show' xs ++ "]"
where
show' :: ShowTypes ts => HList ts -> String
show' HNil = ""
show' (HCons h s) = case s of
HNil -> show h
HCons {} -> show h ++ ", " ++ show' s
答案 1 :(得分:2)
感谢hammar的第二个解决方案,我现在可以提供一种更通用的方法,适用于一般课程(但我认为无论如何他都记得这一点):
type family ConstrainedTypes (a :: [*]) (f :: * -> Constraint) :: Constraint
type instance ConstrainedTypes '[] b = ()
type instance ConstrainedTypes (a ': t) b = (b a, ConstrainedTypes t b)
instance ConstrainedTypes ts Show => Show (HList ts) where
show xs = "[" ++ show' xs ++ "]"
where
show' :: ConstrainedTypes ts Show => HList ts -> String
show' HNil = ""
show' (HCons h s) = case s of
HNil -> show h
HCons {} -> show h ++ ", " ++ show' s
再次感谢您的大力帮助。