Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell记录语法和类型类_Haskell_Class_Types_Record - Fatal编程技术网

Haskell记录语法和类型类

Haskell记录语法和类型类,haskell,class,types,record,Haskell,Class,Types,Record,假设我有两种数据类型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

假设我有两种数据类型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?换句话说,有没有办法将记录语法创建的函数与类型类(getter和setter)连接起来

编辑


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

您可以使用如下代码

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' }

您在代码中建模什么?如果我们对这个问题有更多的了解,我们可以提出一些比嵌入函数式语言中的面向对象设计更简单的建议。

如果您将类型实例设置为可折叠的,那么您将获得一个toList函数,可以将其用作访问器的基础

如果Foldable对您没有任何帮助,那么正确的方法可能是将您想要的接口定义为类型类,并找到一种自动生成派生值的好方法

也许是从做中得到的

deriving(Data)
你可以使用gmap combinators来进行访问。

你想要的,我想这是Haskell中谈论最多的话题之一。目前看来,在如何实施这一计划方面,还没有多少共识

在您的例子中,您似乎可以使用一个异构列表,而不是一个普通的记录,就像在中实现的那样


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

在我看来,这就像是泛型的工作。如果您可以使用不同的新类型标记Int,那么您将能够(使用uniplate、module PlateData)编写:

data Foo=Foo其他派生的东西(数据,可键入)
数据条=导出的另一事物(数据,可类型)
数据选项=F Foo | B Bar
newtype某物=S Int
newtype另一个=整数
newtype Thing=T Int

getAnothers opts=[x | A x如果你有一个单一的数据类型
data FooBar=Foo{x::Int,y::Int}| Bar{x::Int,z::Int}
你就不会有这个问题。如果你的数据类型在不同的模块中,你可以使用
{-#语言消歧记录字段}
。坚持当前设计的原因是什么?谢谢你的回答。你的方法在某种程度上简化了事情。我修改了我的原始问题,以更多地解释我试图解决的实际问题。我同意将OO方法与函数式语言结合使用通常不是最好的方法。我对函数式语言不熟悉d仍在试图打破我的OO思维。:-)关于拥有一个公共记录类型和一个程序特定类型的有趣想法。除了公共和程序(还不确定)之外,我可能有更多的公共分组,但我可以轻松扩展您的方法来支持这一点。(例如,我可以通过StateT作为记录的关联列表传递,每个选项都知道如何按名称查找列表中相应的记录。)
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 ]