Haskell的OOP风格的可显示对象列表? [英] List of showables OOP-style in Haskell?

查看:40
本文介绍了Haskell的OOP风格的可显示对象列表?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想建立一个具有相同属性的不同事物的列表,即它们可以变成字符串.面向对象的方法很简单:定义接口 Showable 并使感兴趣的类实现它.当您不能更改类时,第二点原则上可能会成为问题,但让我们假装情况并非如此.然后创建一个 Showable 的列表,并用这些类的对象填充该列表,而不会产生任何额外的干扰(例如,向上转换通常是隐式完成的).Java 此处提供了概念证明.

I want to build a list of different things which have one property in common, namely, they could be turned into string. The object-oriented approach is straightforward: define interface Showable and make classes of interest implement it. Second point can in principle be a problem when you can't alter the classes, but let's pretend this is not the case. Then you create a list of Showables and fill it with objects of these classes without any additional noise (e.g. upcasting is usually done implicitly). Proof of concept in Java is given here.

我的问题是我在Haskell中有什么选择?下面,我讨论了我尝试过的方法,这些方法并不能真正令我满意.

My question is what options for this do I have in Haskell? Below I discuss approaches that I've tried and which don't really satisfy me.

方法1 :存在感.可行但很丑.

Approach 1: existensials. Works but ugly.

{-# LANGUAGE ExistentialQuantification #-}
data Showable = forall a. Show a => Sh a

aList :: [Showable]
aList = [Sh (1 :: Int), Sh "abc"]

对我来说,这里的主要缺点是在填充列表时必须使用 Sh .这与向上转换操作非常相似,后者是用OO语言隐式完成的.

The main drawback for me here is the necessity for Sh when filling the list. This closely resembles upcast operations which are implicitly done in OO-languages.

更一般的说,已经存在于语言- Show 类型类中的东西的伪包装器 Showable 在我的代码中增加了额外的噪音.不好.

More generally, the dummy wrapper Showable for the thing which is already in the language — Show type class — adds extra noise in my code. No good.

方法2 :强制性.所需,但不起作用.

Approach 2: impredicatives. Desired but does not work.

对我来说,这样的列表最直接的类型就是我真正想要的:

The most straightforward type for such a list for me and what I really desire would be:

{-# LANGUAGE ImpredicativeTypes #-}
aList :: [forall a. Show a => a]
aList = [(1 :: Int), "abc"]

除此之外(我所听到的) ImpredicativeTypes 充其量是脆弱的,最坏的是破坏的"它无法编译:

Besides that (as I heard)ImpredicativeTypes is "fragile at best and broken at worst" it does not compile:

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的类型签名,如果没有它,我会收到更多奇怪的消息:无法推断出文字"1" 引起的(数字a).)

and the same error for "abc". (Note type signature for 1: without it I receive even more weird message: Could not deduce (Num a) arising from the literal ‘1’).

方法3 :排名为N的类型以及某种功能列表(差异列表?).

Approach 3: Rank-N types together with some sort of functional lists (difference lists?).

人们可能宁愿选择更稳定,更广为接受的 RankNTypes ,而不是有问题的 ImpredicativeTypes .这基本上意味着:移动所需的 forall a.显示一个=>超出类型构造函数(即 [] )以转换为普通函数类型.因此,我们需要将列表表示为纯函数.我几乎听不到有这样的表述.我听说的是差异列表.但是在 Dlist 主要类型是旧的 data ,因此我们返回强制性.我没有进一步调查此行,因为我怀疑它会比方法1产生更多冗长的代码.但是,如果您认为不会,请举个例子.

Instead of problematic ImpredicativeTypes one would probably prefer more stable and wide-accepted RankNTypes. This basically means: move desired forall a. Show a => a out of type constructor (i.e. []) to plain function types. Consequently we need some representation of lists as plain functions. As I barely heard there are such representations. The one I heard of is difference lists. But in Dlist package the main type is good old data so we return to impredicatives. I didn't investigate this line any further as I suspect that it could yield more verbose code than in approach 1. But if you think it won't, please give me an example.

底线:您将如何在Haskell中攻击此类任务?您能提供比OO语言更简洁的解决方案(尤其是代替填充列表吗?请参见方法1中的代码注释)?您能否评论上面列出的方法的相关性?

Bottom line: how would you attack such a task in Haskell? Could you give more succinct solution than in OO-language (especially in place of filling a list — see comment for code in approach 1)? Can you comment on how relevant are the approaches listed above?

UPD (基于第一条评论):为了便于阅读,问题当然得到了简化.真正的问题在于如何存储共享相同类型类的东西,即以后可以通过多种方式进行处理( Show 仅具有一种方法,而其他类可以具有多个方法)).这排除了建议在填写列表时立即应用 show 方法的解决方案.

UPD (based on first comments): the question is of course simplified for the purpose of readability. The real problem is more about how to store things which share the same type class, i.e. can be processed later on in a number of ways (Show has only one method, but other classes can have more than one). This factors out solutions which suggest apply show method right when filling a list.

推荐答案

虽然可以使用 HList 样式的解决方案,但是如果只需要使用约束列表,则可以降低复杂性存在,您不需要其他 HList 机制.

The HList-style solutions would work, but it is possible to reduce the complexity if you only need to work with lists of constrained existentials and you don't need the other HList machinery.

这是我在 existentialist 中处理此问题的方式:

{-# 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)

如果您的列表很大,或者可能必须对约束的存在列表进行很多操作,这可能会很有用.

It could be useful if you have a large list or possibly if you have to do lots of manipulations to a list of constrained existentials.

使用这种方法,您也不需要在类型(或元素的原始类型)中编码列表的长度.根据情况的不同,这可能是好事也可能是坏事.如果要保留所有原始类型信息,则可以使用 HList .

Using this approach, you also don't need to encode the length of the list in the type (or the original types of the elements). This could be a good thing or a bad thing depending on the situation. If you want to preserve the all of original type information, an HList is probably the way to go.

此外,如果(例如 Show 的情况)只有一个类方法,那么我建议的方法是将该方法直接应用于列表中的每个项目,如ErikR的答案或phadej的答案中的第一种技术.

Also, if (as is the case of Show) there is only one class method, the approach I would recommend would be applying that method to each item in the list directly as in ErikR's answer or the first technique in phadej's answer.

听起来实际的问题比仅仅显示 Show 的值列表要复杂得多,因此很难给出确切的建议,在没有更具体说明的情况下,其中哪一个最合适?信息.

It sounds like the actual problem is more complex than just a list of Show-able values, so it is hard to give a definite recommendation of which of these specifically is the most appropriate without more concrete information.

但是,其中一种方法可能会很好地工作(除非可以简化代码本身的体系结构,以便一开始就不会遇到问题).

One of these methods would probably work out well though (unless the architecture of the code itself could be simplified so that it doesn't run into the problem in the first place).

这可以概括为更高的种类,例如:

This can be generalized to higher kinds like this:

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 -结果类型的函数列表.

Using this, we can (for example) create a list of functions that have Show-able result types.

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 _ = ()

通过定义,我们可以看到这是一个真实的概括:

We can see that this is a true generalization by defining:

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 可以使用此新的,更通用的公式进行工作,除了更改 example 代码进行任何更改>:> 到>:(即使使用模式同义词也可以避免这种小的更改.我不确定,因为我没有尝试过,有时甚至不使用模式同义词以我不完全了解的方式与存在量化互动).

This allows the original example from the first part of this to work using this new, more general, formulation with no changes to the existing example code except changing :> to >: (even this small change might be able to be avoided with pattern synonyms. I'm not totally sure though since I haven't tried and sometimes pattern synonyms interact with existential quantification in ways that I don't fully understand).

这篇关于Haskell的OOP风格的可显示对象列表?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆