gpt4 book ai didi

haskell - 我怎样才能在 Haskell 中简洁地表示异类求和类型?

转载 作者:行者123 更新时间:2023-12-05 04:34:33 25 4
gpt4 key购买 nike

我正在编写一个程序,将财务报表转码为 a ledger .在那个程序中,我有代表不同事件的类型:

data Withdrawal = Withdrawal { wTarget :: !Text, wAmount :: !Cash, wBalance :: !Cash }
data Fee = { fFee :: !Cash, fBalance :: !Cash }
-- many more

我使用这些类型,因为我有特定于交易类型的函数。

我还想编写一个将 CSV 记录转换为这些类型的事件解析器,因此我创建了一个 Activity 求和类型:

data Activity =
ActivityFee Fee
| ActivityWithdrawal Withdrawal
| -- ...

parseActivity :: CsvRecord -> Activity

那个 Activity 非常样板化。必须为新的事件类型创建一个新的 Activity* 构造函数有点麻烦。

这个问题是否有更惯用或更好的设计模式?如果是 C++,std::variant 会很方便,因为添加新的事件类型不需要添加新的样板构造函数。

我考虑过类型类,但它们的问题是它们不是封闭的,我无法通过模式匹配来创建像 applyActivity::Activity -> Wallet -> Wallet 这样的函数>。我看到我可以将 applyActivity 变成 Activity 类的函数,但问题是只有一个参数使用此模式时,此解决方案才简单明了。如果我们有两个参数,如 foo::(ClassOne a, ClassTwo b) => a -> b -> c,那么 foo 应该属于哪个类就不清楚了.

最佳答案

一个选项是不用定义总和类型,而是生成 parseActivity返回 Wallet -> Wallet表征事件的操作,包裹在一些Parser输入 Alternative 实例。

parseActivity :: CsvRecord -> Parser (Wallet -> Wallet)

你仍然需要定义一个大的 Parser使用一堆 <|> 的值组成了Parser s 用于每个可能的事件。

Wallet -> Wallet 以外的其他操作可以通过使解析器返回函数记录来支持:

data ActivityOps = ActivityOps { 
applyActivity :: Wallet -> Wallet,
debugActivity :: String
}

这仍然不如 sum 类型通用,因为它预先限制了我们可能对事件执行的操作。为了支持新操作,我们需要更改 Parser ActivityOps值(value)。使用 sum 类型,我们只需定义一个新函数。

此解决方案的一个变体是定义一个类型类,例如

class ActivityOps a where
applyActivity :: a -> Wallet -> Wallet
debugActivity :: a -> String

并制作Parser返回某种存在主义,例如:

{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE GADTSyntax #-}
data Activity where
MakeActivity :: ActivityOps a => a -> Activity

这有时是 frowned upon , 但它的好处是能够轻松调用 ActivityOps已知类型事件的方法。

关于haskell - 我怎样才能在 Haskell 中简洁地表示异类求和类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71192855/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com