Haskell 中使用 State-Monad 的类设置方法
我最近看了 Haskell 的 Monad - State。我已经能够创建与这个 Monad 一起操作的函数,但我试图将行为封装到一个类中,基本上我试图在 Haskell 中复制这样的东西:
class A {
public:
int x;
void setX(int newX) {
x = newX;
}
void getX() {
return x;
}
}
如果有人可以提供帮助,我将非常感激这。谢谢!
I've recently had a look at Haskell's Monad - State. I've been able to create functions that operate with this Monad, but I'm trying to encapsulate the behavior into a class, basically I'm trying to replicate in Haskell something like this:
class A {
public:
int x;
void setX(int newX) {
x = newX;
}
void getX() {
return x;
}
}
I would be very grateful if anyone can help with this. Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我首先要指出的是,至少可以说,Haskell 并不鼓励传统的 OO 风格的开发;相反,它具有非常适合“纯函数”操作的功能和特性,而这在许多其他语言中是找不到的;简而言之,尝试从其他(传统语言)“引入”概念通常是一个非常糟糕的主意。
因此,我想到的第一个主要问题是为什么?您肯定想用这个(传统的面向对象概念)类做一些事情吗?
如果这个问题的大致答案是:“我想对某种数据构造进行建模”,那么您最好使用类似的东西,
它演示了纯粹的、不可变的数据结构,以及“getter”函数和破坏性的更新(利用记录语法)。这样,您就可以根据需要将这些“数据结构”映射到新数据结构的函数组合来完成所需的任何工作。
现在,如果您绝对需要某种状态模型(事实上,回答这个问题需要一些经验来准确了解什么是本地状态与全局状态),< em>只有这样你才会深入研究使用 State Monad,比如:(
无耻地摘自 这教程)。请注意,除了“put”和“get”单子函数之外,在状态单子的上下文中还使用了类似的“纯不可变数据结构”,这有助于访问状态单子中包含的状态单子。
最后,我建议您问自己:您真正想通过这个 (OO) 类模型实现什么目标? Haskell 不是典型的面向对象语言,尝试一对一地映射概念只会在短期(也可能是长期)让您感到沮丧。这应该是一个标准的口头禅,但我强烈建议您学习Real World Haskell这本书,其中作者能够深入研究选择任何一种工具或抽象而不是另一种工具或抽象的更详细的“动机”。如果您坚定的话,您可以在 Haskell 中对传统的 OO 构造进行建模,但我不建议这样做 - 除非您有这样做的真正理由。
I would start off by noting that Haskell, to say the least, does not encourage traditional OO-style development; instead, it has features and characteristics that lend themselves well to the sort of 'pure functional' manipulation that you won't really find in many other languages; the short on this is that trying to 'bring over' concepts from other (traditional languages) can often be a very bad idea.
Hence, my first major question that comes to mind is why? Surely you must want to do something with this (traditional OO concept of a) class?
If an approximate answer to this question is: "I'd like to model some sort of data construct", then you'd be better off working with something like
Which demonstrates pure, immutable data structures, along with 'getter' functions and destructive updates (utilizing record syntax). This way, you'd do whatever work you need to do as some combination of functions mapping these 'data constructs' to new data constructs, as appropriate.
Now, if you absolutely needed some sort of model of State (and indeed, answering this question requires a bit of experience in knowing exactly what local versus global state is), only then would you delve into using the State Monad, with something like:
(shamelessly lifted from this tutorial). Note the use of the analogous 'pure immutable data structures' within the context of a state monad, in addition to 'put' and 'get' monadic functions, which facilitate access to the state contained within the State Monad.
Ultimately, I'd suggest you ask yourself: what is it that you really want to accomplish with this model of an (OO) class? Haskell is not your typical OO-language, and trying to map concepts over 1-to-1 will only frustrate you in the short (and possibly long) term. This should be a standard mantra, but I'd highly recommend learning from the book Real World Haskell, where the authors are able to delve into far more detailed 'motivation' for picking any one tool or abstraction over another. If you were adamant, you could model traditional OO constructs in Haskell, but I wouldn't suggest going about doing this - unless you have a really good reason for doing so.
将命令式代码转换为纯函数式上下文需要进行一些排列。
setter 会改变对象。由于懒惰和纯粹,我们不允许直接在 Haskell 中这样做。
也许,如果我们将 State monad 转录为另一种语言,它会更加明显。您的代码是用 C++ 编写的,但因为我至少想要垃圾收集,所以我将在这里使用 java。
由于 Java 从未定义过匿名函数,因此首先我们将为纯函数定义一个接口。
然后我们就可以组成一个纯粹的不可变对类型。
有了这些,我们实际上可以定义 State monad:
现在,状态 monad 内部确实存在 getter 和 setter 的概念。这些称为镜头。 Java 中的基本表示如下:
这个想法是,lens 提供对 getter 的访问,该 getter 知道如何从 A 中提取 B,以及 setter 知道如何获取 B 和一些旧的 A,并替换部分内容A,产生一个新的 A。它不能改变旧的 A,但可以用替换的字段之一构造一个新的 A。
我在最近的波士顿地区 Scala 爱好者会议上就这些问题发表了演讲。您可以在此处观看演示文稿。
回到 Haskell,而不是在命令式的环境中谈论事情。我们可以
从 导入comonad-transformers 或 此处提到的其他镜头库之一。该链接提供了成为有效镜头必须满足的法律。
然后您要寻找的是某种数据类型,例如:
使用镜头
现在您可以编写看起来像
使用 Data.Lens.Lazy。
上面提到的其他镜头库提供类似的组合器。
It takes a bit of permuting to transform imperative code into a purely functional context.
A setter mutates an object. We're not allowed to do that directly in Haskell because of laziness and purity.
Perhaps, if we transcribe the State monad to another language it'll be more apparent. Your code is in C++, but because I at least want garbage collection I'll use java here.
Since Java never got around to defining anonymous functions, first, we'll define an interface for pure functions.
Then we can make up a pure immutable pair type.
With those in hand, we can actually define the State monad:
Now, there does exist a notion of a getter and a setter inside of the state monad. These are called lenses. The basic presentation in Java would look like:
The idea is that a lens provides access to a getter that knows how to extract a B from an A, and a setter that knows how to take a B and some old A, and replace part of the A, yielding a new A. It can't mutate the old one, but it can construct a new one with one of the fields replaced.
I gave a talk on these at a recent Boston Area Scala Enthusiasts meeting. You can watch the presentation here.
To come back into Haskell, rather than talk about things in an imperative setting. We can import
from comonad-transformers or one of the other lens libraries mentioned here. That link provides the laws that must be satisfied to be a valid lens.
And then what you are looking for is some data type like:
with a lens
Now you can write code that looks like
using the combinators from Data.Lens.Lazy.
The other lens libraries mentioned above provide similar combinators.
首先,我同意 Raeez 的观点,这可能是错误的方法,除非你真的知道为什么!如果你想将某个值增加 42(比如说),为什么不编写一个函数来为你做到这一点呢?
这与传统的面向对象思维方式有很大的不同,在传统的面向对象思维方式中,你有一些盒子,里面有值,你把它们拿出来,操纵它们,然后把它们放回去。我会说,直到你开始注意到这个模式“嘿!我总是把一些值当作一个参数,最后返回它稍微修改一下,加上一些其他值,所有的管道都变得混乱了!”你真的不需要
State
monad。 (学习)Haskell 的部分乐趣在于寻找新方法来绕过有状态的 OO 思维!也就是说,如果您绝对想要一个带有
Int
类型的x
的盒子,您可以尝试制作自己的get
和put
变体,如下所示:现在,如果您在 ghci 中评估
runState test (Point 2 9)
,您将得到(12,Point {x = 7, y = 9})
(因为12 = 2 + (2+1) + 7 并且状态中的 x 最后被设置为 7)。如果您不关心返回的点,可以使用evalState
,您将只得到12
。这里还需要做一些更高级的事情,例如使用类型类抽象出
Point
,以防您有多个数据结构,这些数据结构的行为类似于x
但最好留给另一个问题,在我看来!First of all, I agree with Raeez that this probably the wrong way to go, unless you really know why! If you want to increase some value by 42 (say), why not write a function that does that for you?
It's quite a change from the traditional OO mindset where you have boxes with values in them and you take them out, manipulate them and put them back in. I would say that until you start noticing the pattern "Hey! I always take some value as an argument, and at the end return it slightly modified, tupled with some other value and all the plumbing is getting messy!" you don't really need the
State
monad. Part of the fun of (learning) Haskell is finding new ways to get around the stateful OO thinking!That said, if you absolutely want a box with an
x
of typeInt
in it, you could try making your ownget
andput
variants, something like this:Now, if you evaluate
runState test (Point 2 9)
in ghci, you'll get back(12,Point {x = 7, y = 9})
(since 12 = 2 + (2+1) + 7 and thex
in the state gets set to 7 at the end). If you don't care about the returned point, you can useevalState
and you'll get just the12
.There's also more advanced things to do here like abstracting out
Point
with a typeclass in case you have multiple datastructures which have something which behaves likex
but that's better left for another question, in my opinion!