繁体   English   中英

Haskell记录语法和类型类

[英]Haskell record syntax and type classes

假设我有两种数据类型Foo和Bar。 Foo有字段x和y。 条形图有字段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)连接起来?

编辑

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

您需要可扩展记录 ,我收集这些记录是Haskell中最受关注的主题之一。 目前似乎没有就如何实施它达成多少共识。

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

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

你可以使用像这样的代码

data Foo = Foo { fooX :: Int, fooY :: Int } deriving (Show)
data Bar = Bar { barX :: Int, barZ :: Int } deriving (Show)

instance HasX Foo where
  getX = fooX
  setX r x' = r { fooX = x' }

instance HasX Bar where
  getX = barX
  setX r x' = r { barX = x' }

你在代码中建模什么? 如果我们对这个问题有了更多的了解,我们可以建议一些不那么尴尬的东西,而不是这种面向对象的设计。

对我来说似乎是一个泛型工作。 如果你可以使用不同的newtype标记你的Int,那么你就可以编写(使用uniplate,模块PlateData):

data Foo = Foo Something Another deriving (Data,Typeable)
data Bar = Bar Another Thing deriving (Data, Typerable)

data Opts = F Foo | B Bar

newtype Something = S Int
newtype Another = A Int
newtype Thing = T Int

getAnothers opts = [ x | A x <- universeBi opts ]

这将从Opts内的任何地方提取所有的另一个。

也可以进行修改。

如果您创建了Foldable的类型实例,则会获得一个toList函数,您可以将其用作访问者的基础。

如果Foldable不是你的任何东西,那么正确的方法是将你想要的接口定义为类型类,并找出一种自动生成派生值的好方法。

也许是通过做出来的

deriving(Data)

您可以使用gmap组合器来关闭您的访问权限。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM