我想要构建一个有一个共同属性的不同事物的列表,即可以将它们转换为字符串。面向对象的方法很简单:定义接口Showable并使感兴趣的类实现它。第二点在原则上可能是一个问题,当您不能更改类时,让我们假设情况并非如此。然后创建一个Showable列表,并使用这些类的对象填充它,而不需要任何额外的噪声(例如,向上转换通常是隐式的)。概念在Java 就在这里中的证明。
我的问题是我在Haskell有什么选择?下面我将讨论我尝试过的方法,这些方法并不能真正让我满意。
方法1:存在敏感词。但很丑。
{-# LANGUAGE ExistentialQuantification #-}
data Showable = forall a. Show a => Sh a
aList :: [Showable]
aList = [Sh (1 :: Int), Sh "abc"]在这里,我的主要缺点是在填写列表时必须使用Sh。这与在OO语言中隐式执行的向上转换操作非常相似。
更普遍的情况是,已经在语言中的对象的虚拟包装器Showable -- Show类型类--在我的代码中添加了额外的噪声。不是很好。
方法2:非谓语。渴望但不起作用。
对于我和我真正想要的清单来说,最直接的类型是:
{-# LANGUAGE ImpredicativeTypes #-}
aList :: [forall a. Show a => a]
aList = [(1 :: Int), "abc"]此外,(如我所知)ImpredicativeTypes“最好是脆弱的,最坏的是坏的”,它没有编译:
Couldn't match expected type ‘a’ with actual type ‘Int’
‘a’ is a rigid type variable bound by
a type expected by the context: Show a => a同样的错误也适用于"abc"。(注: 1:如果没有它,我会收到更奇怪的消息:Could not deduce (Num a) arising from the literal ‘1’)。
方法3:秩-N类型和某种函数列表(差异列表?)。
与有问题的ImpredicativeTypes不同,人们可能更喜欢更稳定和更广泛接受的RankNTypes。这基本上意味着:将所需的forall a. Show a => a从类型构造函数(即[])移到普通函数类型。因此,我们需要将列表表示为普通函数。我几乎没有听说过有这样的陈述。我听到的是差异列表。但在套餐中,主要类型是好的、老的data,因此我们返回到非预测词。我没有进一步研究这一行,因为我怀疑它会产生比在方法1中更冗长的代码,但是如果您认为它不会,请给我一个例子。
底线:在Haskell,您将如何攻击这样的任务?您能给出比OO语言更简洁的解决方案(尤其是在填写列表时,请参阅方法1中的代码注释)。你能评论一下上面列出的方法有多相关吗?
UPD (基于第一个注释):问题当然是为了可读性而简化的。真正的问题是如何存储共享相同类型类的东西,即以后可以通过多种方式进行处理(Show只有一种方法,而其他类可以有多个方法)。这就排除了建议在填充列表时正确应用show方法的解决方案。
发布于 2015-11-07 21:13:20
HList-style解决方案可以工作,但如果只需要处理受约束的存在元素列表,而不需要其他HList机器,则可以降低复杂性。
下面是我如何在我的套餐中处理这个问题
{-# LANGUAGE ConstraintKinds, ExistentialQuantification, RankNTypes #-}
data ConstrList c = forall a. c a => a :> ConstrList c
| Nil
infixr :>
constrMap :: (forall a. c a => a -> b) -> ConstrList c -> [b]
constrMap f (x :> xs) = f x : constrMap f xs
constrMap f Nil = []这样就可以这样使用:
example :: [String]
example
= constrMap show
(( 'a'
:> True
:> ()
:> Nil) :: ConstrList Show)如果您有一个很大的列表,或者可能需要对一个受约束的存在元素列表进行大量操作,那么它可能是有用的。
使用这种方法,您也不需要在类型(或元素的原始类型)中编码列表的长度。这可能是一件好事,也可能是一件坏事,视情况而定。如果您想要保留所有原始类型的信息,那么HList可能是最好的选择。
此外,如果只有一个类方法(如Show的情况),我建议的方法是将该方法直接应用于列表中的每个项目,就像ErikR的答案或phadej答案中的第一个技术一样。
听起来实际的问题比仅仅列出Show-able值要复杂得多,因此,如果没有更具体的信息,很难明确地推荐其中哪一个是最合适的。
不过,这些方法中的一种可能效果很好(除非代码本身的体系结构可以简化,这样它就不会首先遇到问题)。
泛化为包含在高级类型中的存在性
这可以推广到更高的种类,如:
data AnyList c f = forall a. c a => f a :| (AnyList c f)
| Nil
infixr :|
anyMap :: (forall a. c a => f a -> b) -> AnyList c f -> [b]
anyMap g (x :| xs) = g x : anyMap g xs
anyMap g Nil = []使用它,我们可以(例如)创建一个具有Show-able结果类型的函数列表。
example2 :: Int -> [String]
example2 x = anyMap (\m -> show (m x))
(( f
:| g
:| h
:| Nil) :: AnyList Show ((->) Int))
where
f :: Int -> String
f = show
g :: Int -> Bool
g = (< 3)
h :: Int -> ()
h _ = ()通过定义以下内容,我们可以看出这是一个真正的概括:
type ConstrList c = AnyList c Identity
(>:) :: forall c a. c a => a -> AnyList c Identity -> AnyList c Identity
x >: xs = Identity x :| xs
infixr >:
constrMap :: (forall a. c a => a -> b) -> AnyList c Identity -> [b]
constrMap f (Identity x :| xs) = f x : constrMap f xs
constrMap f Nil = []这使得最初的example可以使用这个新的、更一般的公式,除了将:>更改为>:之外,没有对现有的:>代码进行任何更改(即使是这种小的更改也可以避免使用模式同义词)。不过,我并不完全确定,因为我没有尝试过,有时模式同义词以我不完全理解的方式与存在量化交互作用)。
发布于 2015-11-07 19:19:51
由于计算在Haskell中是懒惰的,那么创建一个实际字符串的列表如何?
showables = [ show 1, show "blah", show 3.14 ]发布于 2015-11-07 20:51:42
如果您真的非常想要,可以使用异构列表。这种方法对Show并不有用,因为它只有一个方法,您所能做的就是应用它,但是如果您的类有多个方法,这可能很有用。
{-# LANGUAGE PolyKinds, KindSignatures, GADTs, TypeFamilies
, TypeOperators, DataKinds, ConstraintKinds, RankNTypes, PatternSynonyms #-}
import Data.List (intercalate)
import GHC.Prim (Constraint)
infixr 5 :&
data HList xs where
None :: HList '[]
(:&) :: a -> HList bs -> HList (a ': bs)
-- | Constraint All c xs holds if c holds for all x in xs
type family All (c :: k -> Constraint) xs :: Constraint where
All c '[] = ()
All c (x ': xs) = (c x, All c xs)
-- | The list whose element types are unknown, but known to satisfy
-- a class predicate.
data CList c where CL :: All c xs => HList xs -> CList c
cons :: c a => a -> CList c -> CList c
cons a (CL xs) = CL (a :& xs)
empty :: CList c
empty = CL None
uncons :: (forall a . c a => a -> CList c -> r) -> r -> CList c -> r
uncons _ n (CL None) = n
uncons c n (CL (x :& xs)) = c x (CL xs)
foldrC :: (forall a . c a => a -> r -> r) -> r -> CList c -> r
foldrC f z = go where go = uncons (\x -> f x . go) z
showAll :: CList Show -> String
showAll l = "[" ++ intercalate "," (foldrC (\x xs -> show x : xs) [] l) ++ "]"
test = putStrLn $ showAll $ CL $
1 :&
'a' :&
"foo" :&
[2.3, 2.5 .. 3] :&
None https://stackoverflow.com/questions/33586720
复制相似问题