Haskell 记录语法和类型类

标签 haskell class types record

假设我有两种数据类型 Foo 和 Bar。 Foo 有字段 x 和 y。 Bar 具有字段 x 和 z。我希望能够编写一个函数,将 Foo 或 Bar 作为参数,提取 x 值,对其执行一些计算,然后返回一个新的 Foo 或 Bar 并相应地设置 x 值。

这是一种方法:

class HasX a where
    getX :: a -> Int
    setX :: a -> Int -> a

data Foo = Foo Int Int deriving Show

instance HasX Foo where
    getX (Foo x _) = x
    setX (Foo _ y) val = Foo val y

getY (Foo _ z) = z
setY (Foo x _) val = Foo x val

data Bar = Bar Int Int deriving Show

instance HasX Bar where
    getX (Bar x _) = x
    setX (Bar _ z) val = Bar val z

getZ (Bar _ z) = z
setZ (Bar x _) val = Bar x val

modifyX :: (HasX a) => a -> a
modifyX hasX = setX hasX $ getX hasX + 5

问题是所有这些 getter 和 setter 都很难写,尤其是当我用具有大量字段的真实数据类型替换 Foo 和 Bar 时。

Haskell 的记录语法提供了一种更好的方式来定义这些记录。但是,如果我尝试像这样定义记录
data Foo = Foo {x :: Int, y :: Int} deriving Show
data Bar = Foo {x :: Int, z :: Int} deriving Show

我会得到一个错误,说 x 被定义了多次。而且,我没有看到任何方法可以使这些成为类型类的一部分,以便我可以将它们传递给 modifyX。

有没有一种干净的方法来解决这个问题,还是我坚持定义自己的 getter 和 setter?换句话说,有没有办法将记录语法创建的函数与类型类(getter 和 setter)连接起来?

编辑

这是我要解决的真正问题。我正在编写一系列相关程序,它们都使用 System.Console.GetOpt 来解析它们的命令行选项。这些程序中会有很多通用的命令行选项,但有些程序可能有额外的选项。我希望每个程序都能够定义一个包含其所有选项值的记录。然后我从一个默认记录值开始,然后通过 StateT monad 和 GetOpt 进行转换,以获得反射(reflect)命令行参数的最终记录。对于单个程序,这种方法非常有效,但我正在尝试找到一种方法来在所有程序中重用代码。

最佳答案

你想要extensible records据我所知,这是 Haskell 中谈论最多的话题之一。似乎目前对于如何实现它并没有太多共识。

在您的情况下,您似乎可以使用像 HList 中实现的异构列表而不是普通记录。 .

话又说回来,这里似乎只有两个级别:普通和程序。因此,也许您应该为公共(public)选项定义一个公共(public)记录类型,并为每个程序定义一个特定于程序的记录类型,并在这些类型的元组上使用 StateT。对于常见的东西,您可以添加组成 fst 的别名。使用公共(public)访问器,因此调用者看不到它。

关于Haskell 记录语法和类型类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1897306/

相关文章:

haskell - 如何可靠地确定 Handle 是否是 Haskell 中的终端?

haskell - Control.Concurrent.Async.race 和 runInteractiveProcess

java - 如何制作一个 Class< 类型的数组?扩展一些东西>?

java - 持有多个继承其方法的实例的类

c++ - 如何在 C++ 中为非常量和本地数据伪造依赖类型?

haskell - 为什么如果 Haskell 列表是左助理,它会更有效

haskell - 如何避免类型类实例的二次爆炸?

php - 从父类返回子类

c++ - 函数模板参数的类型推导

scala - 是否可以在 Scala 中表达以下内容 "type test = Either[List[test], T]"