我正在学习Haskell,遇到了这个问题。 不使用现有类型,我们如何将以下OOP伪代码转换为Haskell?不是在Haskell中模拟OOP概念的方法,而是正确的Haskell方法。
x = 23
i = 1
while i <= x:
if x % i == 0:
print("factor: %s"% i)
i += 1
我的想法之一是,我们不使用类型类,而只是将class MetricQuery { ... }
abstract class Metric[T] {
def computeValue(q: MetricQuery): T
}
class LinkClicksMetric extends Metric[Int] { ... }
class ButtonClicksMetric extends Metric[Int] { ... }
class PostCommentsMetric extends Metric[Int] { ... }
...
query: MetricQuery = ...
metrics: List[Metric[Int]] = ...
results: List[Int] = metrics.map(\x -> x.computeValue(query))
函数设为Haskell数据类型的字段:
computeValue
我认为这可行,只是不确定这是否是“适当的” Haskell方法。
此外,我不知道如何使用特定指标存储其他数据(即,OO术语中的成员变量)。我尝试使用状态类型对度量标准类型构造函数进行参数化,但这导致了不同的特定度量标准类型(例如data MetricQuery = ...
data Metric a = Metric {
computeValue :: MetricQuery -> a
}
linkClicksMetric :: Metric Int
linkClicksMetric = Metric { computeValue = \q -> ... }
buttonClicksMetric :: Metric Int
buttonClicksMetric = Metric { computeValue = \q -> ... }
results =
let query = ...
metrics = ...
in fmap (\x -> computeValue x query) metrics
与Metric Int State1
)。一种想法是将Metric Int State1
设置为data MetricState = ... | ... | ...
的字段,以便每个特定指标都可以定义自己的状态类型。
答案 0 :(得分:4)
您的Metric
数据类型可能会更好,因为它只是类型的同义词。
Metric a = MetricQuery -> a
如果您的特定指标包含其他信息,那么获取此信息的一种方法是部分应用:
buttonClicksMetric :: Button -> Metric Int
buttonClicksMetric button query = ....
之所以可行,是因为通过替换类型同义词,您可以将类型读取为
buttonClicksMetric :: Button -> MetricQuery -> Int
因此,现在您可以通过将Metric Int
传递到Button
来创建buttonClicksMetric
。同样,您可以使用Metric Int
创建另一个linkClicksMetric
并将它们都放在列表中。
但是,如果您想使用buttonClicksMetric
进行其他操作,这些操作需要访问按钮,例如显示按钮。然后,您需要一种数据类型(此设计模式通常称为函数的“验证”:
newtype ButtonClicksMetric = ButtonClicksMetric {getButton :: Button}
(除了:newtype
的工作原理与data
几乎完全相同,不同之处在于您只能具有一个字段和一个构造函数,并且运行时成本为零。底部语义有一些细微的差异不需要担心的值。如果在ButtonClicksMetric中需要更多字段,则可以使用data
。)
buttonClicksMetric
的类型现在变为
buttonClicksMetric :: ButtonClicksMetric -> Metric Int
其他所有功能都一样。