函数式反应式编程语言规范

发布于 2024-11-04 21:09:39 字数 152 浏览 4 评论 0 原文

我正在考虑在某个时候创建​​一个功能性反应框架。我已经阅读了很多相关内容并看到了一些示例,但我想清楚地了解这个框架必须做什么才能被视为 FRP 扩展/dsl。我并不真正关心实施问题或细节等,但更关心完美世界情况下需要什么。

理想的函数式反应式编程语言的关键操作和品质是什么?

I am looking at messing around with creating a functional reactive framework at some point. I have read quite a lot about it and seen a few examples but I wanted to get a clear idea of what this framework would HAVE to do to be considered an FRP extension/dsl. I'm not really concerned with implementation problems or specifics etc but more as to what would be desired in a perfect world situation.

What would be the key operations and qualities of an ideal functional reactive programming language?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

自在安然 2024-11-11 21:09:39

我很高兴您首先询问规范而不是实现。
关于玻璃钢是什么,有很多想法。
从 20 世纪 90 年代初开始(当时我在 Sun Microsystems 和 Microsoft Research 从事交互式图形工作),它就涉及两个属性(a)外延性和(b)时间连续性。
许多人放弃了这两个属性,并用各种实现概念来识别 FRP,所有这些在我看来都不是重点。
为了减少混乱,我希望看到术语“函数式反应式编程”被更准确的“函数式反应式编程”所取代。描述性“指示性连续时间编程”(DCTP),如 Jake McArthur 在 a 中所建议的去年的谈话

我所说的“指示性”是指建立在精确、简单、独立于实现的组合语义的基础上,它准确地指定了每种类型和构建块的含义。
然后,语义的组合性质决定了构建块的所有类型正确组合的含义。
对我来说,外延是心和心。函数式编程的本质,也是实现精确和精确的原因。易于处理的推理,从而为正确性、推导和优化奠定了基础。
Peter Landin 建议使用“指示性”作为模糊术语“函数式”的实质性替代,也是一种区分深度/真正的函数式编程与仅仅是函数式符号的方法。
请参阅此评论了解一些 Landin引用和论文参考。

关于连续时间,请参阅帖子 为什么要使用连续时间进行编程? 以及我在本页 AshleyF 的回答中的引用。
我很惊讶&考虑到计算机的离散性质,连续时间的想法在某种程度上是不自然的或不可能实现的。
这种思路让我觉得很奇怪,特别是当来自 Haskellers 时,原因如下:

  • 使用惰性函数语言,我们随意地在有限上使用无限数据进行编程 机器。结果我们得到了可爱的模块化,正如 John Hughes 的经典论文 为什么函数式编程很重要
  • 在连续空间中编程的例子有很多,例如矢量图形,还有 Pan 之类的东西。
  • 我喜欢我的程序反映我如何看待问题空间而不是执行程序的机器,并且我倾向于期望其他高级语言程序员也有这种偏好。
    (“当程序需要关注不相关的内容时,编程语言就是低级的。” - Alan Perlis)

自从 TBAGActiveVRML(第一个 DCTP/FRP 系统)和稍后Fran
正确实施很容易。
连续建模动画的功能实现论文中描述了几种不同的方法。
高效(并且仍然正确!)实现连续时间是另一回事,尤其是避免重新计算不变的值。
(请参阅论文推拉函数反应式编程。 )

相关备注,请参阅我对反应式编程和函数式反应式编程之间的区别以及什么是(函数式)反应式编程? 更新:有关连续时间为何如此重要的更多信息,请参阅这些注释更新:另请参阅我 2015 年的演讲 FRP 的本质和起源(以及链接的相关讨论)。

祝您探索顺利,如果您有任何疑问,请告诉我。
我的联系信息位于我的主页

I'm glad you're starting by asking about a specification rather than implementation first.
There are a lot of ideas floating around about what FRP is.
From the very start in the early 90's (when I was working in interactive graphics at Sun Microsystems and then Microsoft Research), it has been about two properties (a) denotative and (b) temporally continuous.
Many folks drop both of these properties and identify FRP with various implementation notions, all of which are beside the point in my perspective.
To reduce confusion, I would like to see the term "functional reactive programming" replaced by the more accurate & descriptive "denotative, continuous-time programming" (DCTP), as suggested by Jake McArthur in a conversation last year.

By "denotative", I mean founded on a precise, simple, implementation-independent, compositional semantics that exactly specifies the meaning of each type and building block.
The compositional nature of the semantics then determines the meaning of all type-correct combinations of the building blocks.
For me, denotative is the heart & essence of functional programming, and is what enables precise & tractable reasoning and thus a foundation for correctness, derivation, and optimization.
Peter Landin recommended "denotative" as a substantive replacement to the fuzzier term "functional" and a way to distinguish deeply/genuinely functional programming from merely functional-looking notations.
See this comment for some Landin quotes and a paper reference.

About continuous time, see the post Why program with continuous time? and my quote in AshleyF's answer on this page.
I'm surprised over & over by hearing the claim that the idea of continuous time is somehow unnatural or impossible to implement, considering the discrete nature of computers.
This line of thinking strikes me as bizarre, especially when coming from Haskellers, for a few reasons:

  • Using lazy functional languages, we casually program with infinite data on finite machines. We get lovely modularity as a result, as illustrated in John Hughes's classic paper Why Functional Programming Matters.
  • There are many examples of programming in continuous space, for instance, vector graphics, but also things like Pan.
  • I like my programs to reflect how I think about the problem space rather than the machine that executes the programs, and I tend to expect other high-level language programmers to share that preference.
    ("A programming language is low level when its programs require attention to the irrelevant." - Alan Perlis)

I've been making libraries for programming with continuous time since TBAG and ActiveVRML (the first DCTP/FRP system) and later Fran.
It's easy to implement correctly.
A few different approaches are described in the paper Functional Implementations of Continuous Modeled Animation.
Implementing continuous time efficiently (and still correctly!) is another matter, especially avoidance of recomputing unchanging values.
(See the paper Push-pull functional reactive programming.)

For related remarks, please see my answer to The difference between Reactive and Functional-Reactive programming and to What is (functional) reactive programming? Update: For more on why continuous time matters, see these notes. Update: See also, my 2015 talk The essence and origins of FRP (and the related talks linked there).

Good luck with your exploration, and please let me know if you have any questions.
My contact info is on my home page.

洋洋洒洒 2024-11-11 21:09:39

我想您可能已经看过 Matthias Felleisen 的关于函数式 I/O 的演讲并阅读了 他的论文。我认为他的做法非常务实且美好。希望您也偶然发现了 Conal Elliott 的一些出色作品。

我个人的要求是系统是完全纯净的。也就是说,所有行为均由纯 world->world 函数定义,所有实现或可视化均由 world->visual 函数定义;其中视觉是系统输出的一些静态描述。

我的另一个主要功能是历史调试器。维护世界状态的历史并能够从任何时间点重播应该是相对简单的。

一个非常有趣的研究领域(我相信这是一个未解决的问题)是使用连续时间,而不是在一些离散时钟滴答上迭代world->world函数。我曾经写过一些关于 FRP 的博客文章Conal Elliott 留下了以下发人深省的评论:

我喜欢指示性/功能性
方法,用于可组合性和
语义清晰。对于同样的
原因,我更喜欢连续时间&
离散时间上的空间 &空间。在
所有这些情况,越少
类似机器的配方很好
将“内容”与“如何”分开
基于机器的演示。

解决这个问题,你就会成为英雄!

I assume you've probably seen Matthias Felleisen’s talk on Functional I/O and read his paper. I think his is a very pragmatic and beautiful approach. Hopefully you've also stumbled onto some of Conal Elliott's excellent work.

My personal requirements would be that the system is completely pure. That is, all behavior is defined by pure world->world functions and all realization or visualization is defined by world->visual functions; where visual is some static description of the output from the system.

My other primary feature would be a historical debugger. It should be relatively trivial to maintain a history of world states and be able to replay from any point in time.

One area of extremely interesting research (I believe an unsolved problem) would be to use continuous time rather than iterating the world->world functions upon some discrete clock ticks. I once did a few blog posts on FRP and Conal Elliott left the following thought provoking comment:

I like denotative/functional
approaches, for composability &
semantic clarity. For the same
reasons, I prefer continuous time &
space over discrete time & space. In
all of these cases, the less
machine-like formulation nicely
separates the what from the how of its
machine-based presentation.

Solve that and you'll be a hero!

桃扇骨 2024-11-11 21:09:39

好吧,除非完美世界指的是心灵感应计算机(哎呀!),那么您将需要某种方式来处理用户 I/O -我假设像正交持久性这样的东西已经包含了更无聊的文件 I/O...

让我们从输入开始...因为它已经有了一个解决方案。摘自 Conal Elliott 和 Paul Hudak 的开创性论文第 4 页(共 11 页)功能响应式动画

lbp; rbp : Time → Event Event ( )

在 Haskell 中,看起来像这样:

 -- read left and right mouse button-press events
lbp, rbp :: Time -> Event (Event ())

因此对于键盘输入:

kbd :: Time -> Event Char.

其他输入可以在类似的时尚。

那么……输出呢?实际的单词并没有出现在论文的任何地方(“I/O”也没有出现)——我们必须自己解决这个问题。但这一次,是我们的 Haskell 翻译:

lbp, rbp :: Time -> Event (Event ())

提供提示 - Event () - unit-event。这可以作为发送 Char 的结果,出现在屏幕上的某个位置:

viewChar :: Char -> Time -> Event ()

同样,可以使用类似的技术处理其他输出。


...那是什么 - 它不是外延的?
因为 viewChar 是...什么 - 不纯
如果是这样,那就意味着 lbprbp 也是不纯的 - 您确定吗?

好吧...让我们有一个类型来接收一系列鼠标按钮按下或其他事件:

type Intake a = [a]

lpb, rbp :: Intake (Event (Event ())

更好吗?好的!嗯,有点——
如果拔掉鼠标会发生什么?那
可以将程序的某些部分置于旋转状态以等待输入(并且使用 [] 将永久结束该系列 - 不再需要按下按钮!)。

我们需要更改Intake

data Intake a = None (Intake a) | Next a (Intake a) 

现在拔掉鼠标会导致None ...
出现,程序可以检测到并做出相应反应,例如产生其操作系统线程,
那么,输出呢

?好吧,输出设备通常也可以拔掉。从Intake得到提示:

data Outlet a = Wait (Outlet a) | Went (… (Outlet a) …) 

这类似于拔掉输入设备 - 当遇到
Wait ...时,程序可以暂停传输。

那么 Went 的类型应该是什么?嗯,Outlet 逐渐接受值,以便在需要时允许出现 Wait ... - 接受每个值应该向我们提供其余的 Output >。因此:

data Outlet a = Wait (Outlet a) | Went (a -> Outlet a)

总而言之:

data Intake a = None (Intake a) | Next a (Intake a)

lbp, rbp :: Intake (Event (Event ())


data Outlet a = Wait (Outlet a) | Went (a -> Outlet a)

viewChar :: Outlet Char

那么这一切都有效吗?如果您不确定,请参阅 的第 20.4.2 节(第 86 页,共 263 页) Fudgets - 纯功能流程与图形用户界面的应用 作者:Magnus Carlsson 和 Thomas Hallgren - 如果 IntakeOutlet 看起来可疑,那么在那里可以看到的内容也是如此,在报纸上...


Well, unless by perfect world you mean telepathic computers (yikes!), then you'll require some way to process user I/O - I'll assume something like orthogonal persistence has subsumed the more boring file I/O...

Let's start with input...because it already has one solution. From page 4 of 11 in Conal Elliott's and Paul Hudak's pioneering paper Functional Reactive Animation:

lbp; rbp : Time → Event Event ( )

which, in Haskell, would look something like:

 -- read left and right mouse button-press events
lbp, rbp :: Time -> Event (Event ())

So for input from the keyboard:

kbd :: Time -> Event Char.

Other inputs can be dealt with in similar fashion.

So...what about output? The actual word doesn't appear anywhere in the paper (neither does "I/O" for that matter) - we'll have to figure this one out ourselves. But this time, it's our Haskell translation:

lbp, rbp :: Time -> Event (Event ())

providing the hint - Event () - the unit-event. That can serve as the result of sending a Char off, to appear somewhere on your screen:

viewChar :: Char -> Time -> Event ()

Again, other outputs can be dealt with using similar techniques.


...what's that - it isn't denotative?
Because viewChar is...what - impure?
If so, that means lbp and rbp are also impure - are you sure about this?

Alright...let's have a type for taking in a series of those mouse button-press, or other events:

type Intake a = [a]

lpb, rbp :: Intake (Event (Event ())

Is that any better? Good! Well, sort of -
what happens if the mouse is unplugged? That
could put parts of a program into a spin waiting for input (and using [] would permanently end the series - no more button presses!).

We need to change Intake:

data Intake a = None (Intake a) | Next a (Intake a) 

Now unplugging the mouse results in None …
appearing, which a program can detect and react accordingly e.g. yielding its OS thread,
suspending itself, etc.

So, what about output? Well, output devices can often be unplugged too. Taking a hint from Intake:

data Outlet a = Wait (Outlet a) | Went (… (Outlet a) …) 

It's similar to unplugging an input device - upon encountering
Wait …, a program can pause transmission.

So what should the type of Went be? Well, an Outlet accepts values incrementally to allow Wait … to appear if needed - the accepting of each value should present us with the rest of the Output. Therefore:

data Outlet a = Wait (Outlet a) | Went (a -> Outlet a)

Bringing that altogether:

data Intake a = None (Intake a) | Next a (Intake a)

lbp, rbp :: Intake (Event (Event ())


data Outlet a = Wait (Outlet a) | Went (a -> Outlet a)

viewChar :: Outlet Char

So is all this valid? If you're not sure, see section 20.4.2 (page 86 of 263) of Fudgets - Purely Functional Processes with applications to Graphical User Interfaces by Magnus Carlsson and Thomas Hallgren - if Intake and Outlet look dubious then so is what can be seen there, in the paper...


~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文