Haskell记录语法和类型类 [英] Haskell record syntax and type classes

查看:175
本文介绍了Haskell记录语法和类型类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有两个数据类型Foo和Bar。 Foo有字段x和y。栏有字段x和z。我想能够写一个函数,它接受Foo或Bar作为参数,提取x值,执行一些计算,然后返回一个新的Foo或Bar相应的x值设置。

Suppose that I have two data types Foo and Bar. Foo has fields x and y. Bar has fields x and z. I want to be able to write a function that takes either a Foo or a Bar as a parameter, extracts the x value, performs some calculation on it, and then returns a new Foo or Bar with the x value set accordingly.

这里有一种方法:

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与现实世界数据类型有很多字段。

The problem is that all those getters and setters are painful to write, especially if I replace Foo and Bar with real-world data types that have lots of fields.

Haskell的记录语法提供了一个更好的方式定义这些记录。但是,如果我尝试定义这样的记录

Haskell's record syntax gives a much nicer way of defining these records. But, if I try to define the records like this

data Foo = Foo {x :: Int, y :: Int} deriving Show
data Bar = Foo {x :: Int, z :: Int} deriving Show

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

I'll get an error saying that x is defined multiple times. And, I'm not seeing any way to make these part of a type class so that I can pass them to modifyX.

有一个很好的干净的解决方法问题,还是我困住定义我自己的getters和setters?换句话说,是否有一种将记录语法创建的函数与类型类(getter和setter)连接的方法?

Is there a nice clean way of solving this problem, or am I stuck with defining my own getters and setters? Put another way, is there a way of connecting the functions created by record syntax up with type classes (both the getters and setters)?

EDIT

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

Here's the real problem I'm trying to solve. I'm writing a series of related programs that all use System.Console.GetOpt to parse their command-line options. There will be a lot of command-line options that are common across these programs, but some of the programs may have extra options. I'd like each program to be able to define a record containing all of its option values. I then start with a default record value that is then transformed through a StateT monad and GetOpt to get a final record reflecting the command-line arguments. For a single program, this approach works really well, but I'm trying to find a way to re-use code across all of the programs.

推荐答案

您希望可扩展记录,我认为这是最多的谈论Haskell中的主题。似乎目前对于如何实现它并没有太多的共识。

You want extensible records which, I gather, is one of the most talked about topics in Haskell. It appears that there is not currently much consensus on how to implement it.

在你的情况下,它似乎可能,而不是一个普通的记录,你可以使用异类列表在 HList 中实施。

In your case it seems like maybe instead of an ordinary record you could use a heterogeneous list like those implemented in HList.

然后再次,似乎你只有两个层次:common和program。所以也许你应该为每个程序定义公共选项和程序特定记录类型的公共记录类型,并在这些类型的元组上使用StateT。对于常见的东西,你可以添加用公共访问器组成 fst 的别名,所以它对调用者是不可见的。

Then again, it seems you only have two levels here: common and program. So maybe you should just define a common record type for the common options and a program-specific record type for each program, and use StateT on a tuple of those types. For the common stuff you can add aliases that compose fst with the common accessors so it's invisible to callers.

这篇关于Haskell记录语法和类型类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆