如何为 Haskell 中的异构列表定义 Show
How to define Show for heterogenous lists in Haskell
我正在研究 Haskell 可以通过阅读 Thinking with types
.
为依赖类型提供什么
这是一个定义为 GADT 的异构列表。
data HList (ts :: [Type]) where
HNil :: HList '[]
(:#) :: t -> HList ts -> HList (t ': ts)
infixr 5 :#
instance Eq (HList '[]) where
HNil == HNil = True
instance (Eq t, Eq (HList ts)) => Eq (HList (t ': ts)) where
(a :# as) == (b :# bs) = a == b && as == bs
instance Ord (HList '[]) where
compare HNil HNil = EQ
instance (Ord t, Ord (HList ts)) => Ord (HList (t ': ts)) where
compare (a :# as) (b :# bs) = case compare a b of
EQ -> compare as bs
x -> x
instance Show (HList '[]) where
show HNil = "[]"
instance (Show t, Show (HList ts)) => Show (HList (t ': ts)) where
show xs = "[" ++ (show' xs "") ++ "]"
where
show' (y :# ys) prefix = prefix ++ (show y) ++ rest
where rest = case of
HNil -> ""
_ -> show' ys ", "
这是我得到的错误。
• Occurs check: cannot construct the infinite type: ts2 ~ t1 : ts2
Expected type: HList ts2 -> [Char] -> [Char]
Actual type: HList ts1 -> p -> p1
• In the expression: show' ys ", "
In a case alternative: _ -> show' ys ", "
In the expression:
case ys of
HNil -> ""
_ -> show' ys ", "
• Relevant bindings include
ys :: HList ts2 (bound at src/Lib.hs:43:25)
y :: t1 (bound at src/Lib.hs:43:20)
|
46 | _ -> show' ys ", "
我阅读了 HList 源代码,我认为他们所做的是一个丑陋的 hack,他们对从 show ys
返回的字符串进行模式匹配,如果它等于 []
那么基本上停止递归,我认为这是一种 hack。
如果您定义自己的自定义 class 并在该自定义上递归,会更容易。这是因为我们无法将 prefix
参数传递给标准 show
方法,但这在自定义 class.
中不再是问题
class HShow ts where
hshow :: HList ts -> String -> String
instance HShow '[] where
hshow HNil _ = ""
instance (Show t, HShow ts) => HShow (t ': ts) where
hshow (y :# ys) p = p ++ show y ++ hshow ys ", "
之后加上括号就可以得到标准的Show
class实例:
instance HShow ts => Show (HList ts) where
show xs = "[" ++ hshow xs "" ++ "]"
test :: HList '[ Int, Char, Bool ]
test = 42 :# 'a' :# True :# HNil
-- > test
-- [42, 'a', True]
基于类型族的替代方案。不是特别简单。
type family All (c :: a -> Constraint) (xs :: [a]) :: Constraint where
All c '[] = ()
All c (x ': xs) = (c x, All c xs)
hshow :: All Show ts => HList ts -> String
hshow HNil = ""
hshow (x :# HNil) = show x
hshow (x :# xs ) = show x ++ ", " ++ hshow xs
instance All Show ts => Show (HList ts) where
show xs = "[" ++ hshow xs ++ "]"
我正在研究 Haskell 可以通过阅读 Thinking with types
.
这是一个定义为 GADT 的异构列表。
data HList (ts :: [Type]) where
HNil :: HList '[]
(:#) :: t -> HList ts -> HList (t ': ts)
infixr 5 :#
instance Eq (HList '[]) where
HNil == HNil = True
instance (Eq t, Eq (HList ts)) => Eq (HList (t ': ts)) where
(a :# as) == (b :# bs) = a == b && as == bs
instance Ord (HList '[]) where
compare HNil HNil = EQ
instance (Ord t, Ord (HList ts)) => Ord (HList (t ': ts)) where
compare (a :# as) (b :# bs) = case compare a b of
EQ -> compare as bs
x -> x
instance Show (HList '[]) where
show HNil = "[]"
instance (Show t, Show (HList ts)) => Show (HList (t ': ts)) where
show xs = "[" ++ (show' xs "") ++ "]"
where
show' (y :# ys) prefix = prefix ++ (show y) ++ rest
where rest = case of
HNil -> ""
_ -> show' ys ", "
这是我得到的错误。
• Occurs check: cannot construct the infinite type: ts2 ~ t1 : ts2
Expected type: HList ts2 -> [Char] -> [Char]
Actual type: HList ts1 -> p -> p1
• In the expression: show' ys ", "
In a case alternative: _ -> show' ys ", "
In the expression:
case ys of
HNil -> ""
_ -> show' ys ", "
• Relevant bindings include
ys :: HList ts2 (bound at src/Lib.hs:43:25)
y :: t1 (bound at src/Lib.hs:43:20)
|
46 | _ -> show' ys ", "
我阅读了 HList 源代码,我认为他们所做的是一个丑陋的 hack,他们对从 show ys
返回的字符串进行模式匹配,如果它等于 []
那么基本上停止递归,我认为这是一种 hack。
如果您定义自己的自定义 class 并在该自定义上递归,会更容易。这是因为我们无法将 prefix
参数传递给标准 show
方法,但这在自定义 class.
class HShow ts where
hshow :: HList ts -> String -> String
instance HShow '[] where
hshow HNil _ = ""
instance (Show t, HShow ts) => HShow (t ': ts) where
hshow (y :# ys) p = p ++ show y ++ hshow ys ", "
之后加上括号就可以得到标准的Show
class实例:
instance HShow ts => Show (HList ts) where
show xs = "[" ++ hshow xs "" ++ "]"
test :: HList '[ Int, Char, Bool ]
test = 42 :# 'a' :# True :# HNil
-- > test
-- [42, 'a', True]
基于类型族的替代方案。不是特别简单。
type family All (c :: a -> Constraint) (xs :: [a]) :: Constraint where
All c '[] = ()
All c (x ': xs) = (c x, All c xs)
hshow :: All Show ts => HList ts -> String
hshow HNil = ""
hshow (x :# HNil) = show x
hshow (x :# xs ) = show x ++ ", " ++ hshow xs
instance All Show ts => Show (HList ts) where
show xs = "[" ++ hshow xs ++ "]"