Maintaining complex state in Haskell

南楼画角 提交于 2019-11-29 18:56:46

The answer is functional reactive programming (FRP). It it a hybrid of two coding styles: component state management and time-dependent values. Since FRP is actually a whole family of design patterns, I want to be more specific: I recommend Netwire.

The underlying idea is very simple: You write many small, self-contained components each with their own local state. This is practically equivalent to time-dependent values, because each time you query such a component you may get a different answer and cause a local state update. Then you combine those components to form your actual program.

While this sounds complicated and inefficient it's actually just a very thin layer around regular functions. The design pattern implemented by Netwire is inspired by AFRP (Arrowized Functional Reactive Programming). It's probably different enough to deserve its own name (WFRP?). You may want to read the tutorial.

In any case a small demo follows. Your building blocks are wires:

myWire :: WireP A B

Think of this as a component. It is a time-varying value of type B that depends on a time-varying value of type A, for example a particle in a simulator:

particle :: WireP [Particle] Particle

It depends on a list of particles (for example all currently existing particles) and is itself a particle. Let's use a predefined wire (with a simplified type):

time :: WireP a Time

This is a time-varying value of type Time (= Double). Well, it's time itself (starting at 0 counted from whenever the wire network was started). Since it doesn't depend on another time-varying value you can feed it whatever you want, hence the polymorphic input type. There are also constant wires (time-varying values that don't change over time):

pure 15 :: Wire a Integer

-- or even:
15 :: Wire a Integer

To connect two wires you simply use categorical composition:

integral_ 3 . 15

This gives you a clock at 15x real time speed (the integral of 15 over time) starting at 3 (the integration constant). Thanks to various class instances wires are very handy to combine. You can use your regular operators as well as applicative style or arrow style. Want a clock that starts at 10 and is twice the real time speed?

10 + 2*time

Want a particle that starts and (0, 0) with (0, 0) velocity and accelerates with (2, 1) per second per second?

integral_ (0, 0) . integral_ (0, 0) . pure (2, 1)

Want to display statistics while the user presses the spacebar?

stats . keyDown Spacebar <|> "stats currently disabled"

This is just a small fraction of what Netwire can do for you.

I know this is old topic. But I am facing the same problem right now while trying to implement Rail Fence cipher exercise from exercism.io. It is quite disappointing to see such a common problem having such poor attention in Haskell. I don't take it that to do some as simple as maintaining state I need to learn FRP. So, I continued googling and found solution looking more straightforward - State monad: https://en.wikibooks.org/wiki/Haskell/Understanding_monads/State

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!