1

在项目中,我有几种不同的类型,定义在不同的模块中,每个都有相关的功能(功能名称相同,含义非常相似,所以以下有意义)。现在我想创建一个列表,其中可以(同时)拥有所有这些类型的实例。我能想到的唯一可能是这样的:

data Common = A{...} | B{...} | ...

但这意味着将定义保存在一个地方,而不是在不同的模块中(对于 A、B、...)。有一个更好的方法吗?

UPD

我对haskell很陌生,写了一些与我的学习相关的程序。在这种情况下,我有不同的FormalLanguage定义方法:FiniteAutomataGrammars等等。它们中的每一个都有共同的功能(isAccepted, representation, ...),所以有一个列表似乎合乎逻辑,其中元素可以是这些类型中的任何一种。

4

2 回答 2

6

通过假设正确的解决方案是将不同的类型存储在列表中,您将 OOP 思维方式带入 Haskell。我将从检验这个假设开始。

通常我们将不同类型存储在同构列表中,因为它们支持公共接口。为什么不直接将通用接口分解出来并将其存储在列表中呢?

不幸的是,您的问题没有描述该通用接口是什么,因此我将仅介绍一些常见示例作为演示。

第一个例子是一堆值,xyz,它们都支持Show具有签名的函数:

(Show a) => a -> String

show我们可以直接调用值并将结果字符串存储在列表中,而不是存储我们稍后要显示的类型:

list = [show x, show y, show z] :: String

过早调用不会受到惩罚,因为 Haskell 是一种惰性语言,并且在我们真正需要字符串之前show不会实际评估s。show

或者该类型可能支持多种方法,例如:

class Contrived m where
    f1 :: m -> String -> Int
    f2 :: m -> Double

我们可以将上述形式的类转换为等效的字典,其中包含将方法部分应用于我们的值的结果:

data ContrivedDict = ContrivedDict {
    f1' :: String -> Int,
    f2' :: Double }

...我们可以使用这个字典将任何值打包到我们期望它支持的公共接口中:

buildDict :: (Contrived m) => m -> ContrivedDict
buildDict m = ContrivedDict { f1' = f1 m, f2' = f2 m }

然后我们可以将这个通用接口本身存储在列表中:

list :: [buildDict x, buildDict y, buildDict z]

同样,我们没有存储不同类型的值,而是将它们的共同元素分解出来存储在列表中。

然而,这个技巧并不总是奏效。病态的例子是任何期望两个相同类型的操作数的二元运算符,例如类中的(+)运算符Num,它具有以下类型:

(Num a) => a -> a -> a

据我所知,没有很好的基于字典的解决方案来部分应用二元运算并将其存储以保证将其应用于相同类型的第二个操作数。在这种情况下,存在类型类可能是唯一有效的方法。但是,我建议您尽可能坚持使用基于字典的方法,因为它允许比基于类型类的方法更强大的技巧和转换。

有关此技术的更多信息,我建议您阅读 Luke Palmer 的文章:Haskell Antipattern: Existential Typeclass

于 2012-09-22T14:49:22.713 回答
5

有几种可能:

可能性一:

data Common = A AT | B BT | C CT

AT、BT 和 CT 在各自的模块中描述

可能性2:

{-# LANGUAGE ExistentialQuantification #-}

class CommonClass a where
    f1 :: a -> Int

data Common = forall a . CommonClass a => Common a

这与 OOP 超类几乎相同,但您不能进行“向下转型”。然后,您可以为所有模块中的公共类的成员声明实现。

@Gabriel Gonzalez 建议的可能性 3:

data Common = Common {
     f1 :: Int
}

因此,您的模块通过使用闭包对“私有”部分进行抽象来实现通用接口。

然而,Haskell 设计通常与 OOP 设计完全不同。虽然可以在 Haskell 中实现每个 OOP 技巧,但它可能不是惯用的,所以正如@dflemstr 所说,欢迎提供有关您的问题的更多信息。

于 2012-09-22T10:59:01.460 回答