Geo*_*rge 4 lisp haskell late-binding
AFAIK,这是LISP方言的标志性特征:例如,假设我们启动了一个正在进行的LISP程序,该程序重复调用一些名为的函数func.然后我们可以进入lisp REPL,更改定义func,然后在程序下次调用时,程序func的行为将被适当地改变为live.
我相信这个属性被称为后期绑定,虽然我不确定我是否正确.
后期绑定的好处是它允许您在程序仍在运行时重构甚至完全更改程序.这在生物学上类似于这样一个事实,即随着年龄的增长,我们体内的几乎每个细胞都会在足够长的时间范围内被新的细胞取代(当然我们从未注意到).它可以使系统非常灵活.
问题:在Haskell中有什么方法可以做类似的事情吗?如果Haskell不允许这样做,那么它是否有充分的理由(即,是否需要进行更大的权衡才能使其变得有价值)?
理解声明性编程的一个好方法是将其视为永恒的.所以没有"下一次程序打电话func".如果程序在某种低级操作意义上的"下一次"表现不同,那将违反纯度.当我们走出程序时,保持我们的FP彩色镜片牢固到位,我们注意到你不能"改变"的定义func,而你正在做的是制作一个具有不同定义的新程序func.所以,当你"改变"的定义时,你所说的func就是放弃当前程序并运行一个新程序.Haskell库就像halive和dyre一样工作.
该IO单子确实让我们一"之前"和"之后"的模式,所以在它里面,我们可以谈论"变化"的东西.更改"the"程序仍然具有与上述相同的问题,但我们当然可以拥有一个包含我们更新的一堆代码的引用单元.实际上,这就是Lisp的(天真实现)中发生的事情.您实际上可以手动执行此操作,例如,定义IORef或MVar保留要更改的功能并根据需要进行更新.现在的问题是你通常想要更新到全新的功能,所以我们需要有一种方法来描述新功能或者动态加载功能.前者对应于有一个可用的解释器,名义上是提示和mueval所做的(尽管它们实际上更像下面的工作).执行后者是动态代码加载,像插件,rts-loader和动态加载器这样的库可以做到这一点.
或者,您可以采用视图(或实际构造它),使代码的某些部分作为协同进程运行.此时,您可以使用标准IPC机制.在此上下文中"调用函数"仅意味着发送消息.
最终,这些都没有提供像Lisp这样的体验.Haskell语言本身不提供"地点"的机制或概念,其中"存储"定义然后可以更新.同样,在概念上,在Lisp中,每个定义都存储在一个可变单元中,并且每个函数调用首先取消引用该可变单元以获得当前定义.其结果是我所提到的每个库和技术需要你提前计划的时间,其中的变化点或要重新加载或者,至少是,什么是要重新加载.没有"附加"到正在运行的Haskell进程或"破解"到调试器中的概念.从技术上讲,使用dyre技术可以重新启动一个用完全不同的语言编写的可执行文件,更不用说任意更改的Haskell可执行文件了.
据我所知,haskell 无法支持这一点,除非您将此函数包装在 IO 或状态 monad 中。
但在纯粹的程序中,重新分配 的定义func会破坏引用透明性——这正是 Haskell 成为一种安全语言的原因之一。此外,破坏引用透明度将使惰性求值变得不可能,因为在更新之前或之后求值函数会产生两个不同的结果,并使程序具有不确定性。
现在,如果您查看GHCi,那么您会发现可以重新分配变量 - 如果您尝试一下
$ > ghci
...
Prelude > let a = (+)
Prelude > let a = (*)
Run Code Online (Sandbox Code Playgroud)
是有效的,但我相信这种let绑定存在IO- 它允许产生诸如重新分配之类的副作用。