javascript - 如何在 JS 中实现类 IO 类型的 MonadFix 实例?
问题描述
我在尝试将以下 Haskell 代码转换为 Javascript 时完全迷失了:
instance MonadFix IO where
mfix f = do
var <- newEmptyMVar -- 1
ans <- unsafeInterleaveIO $ takeMVar var -- 2
result <- f ans -- 3
putMVar var result -- 4
return result -- 5
让我们根据我们的小时间旅行类比逐行遍历:
- 创建一个空的可变变量。
- 预测将包含在该可变变量中的未来值。
- 使用预测的未来值调用函数 f。
- 将 f 的结果存储在变量中,从而实现第 2 行所要求的预言。
- 返回那个结果。
我拥有的是一种特殊Lazy
类型,可以处理(或更确切地说是推迟)同步IO
。但是,我想用值递归从直接递归中抽象出来:
const record = (type, o) =>
(o[Symbol.toStringTag] = type.name || type, o);
const Lazy = lazy => record(Lazy, {get lazy() {
delete this.lazy
return this.lazy = lazy();
}});
// Monad
const lazyChain = mx => fm =>
Lazy(() => fm(mx.lazy).lazy);
const lazyChain_ = fm => mx =>
Lazy(() => fm(mx.lazy).lazy);
const lazyOf = x => Lazy(() => x);
// mfix = chain_ => fm => chain_(fm) (mfix(chain_) (fm)) ???
// MAIN
const trace = x => (console.log(x), x);
const app_ = x => f => trace(f(x));
const readLine = s => Lazy(() => window.prompt(s));
const fac = x => x === 0 ? 1 : x * fac(x - 1),
tri = x => x === 0 ? 0 : x + tri(x - 1);
const foo = lazyChain(readLine("enter true/false")) (s => {
return lazyOf(s === "true"
? fac : tri);
});
const bar = x => lazyChain(foo) (y => lazyOf(app_(x) (y)));
const main = bar(5);
console.log("effect has not yet been unleashed..");
main.lazy;
如何在 Javascript 中实现Lazy
实例?MonadFix
解决方案
你的构造有一个thunkLazy
的表示,它恰好形成一个单子(单子应用程序只是严格的函数应用程序,正如你所写的那样),但是你没有 的表示IO
,所以你基本上只是在使用unsafePerformIO
到处。
在您的模型中,每个 I/O 操作只能执行一次,所以我希望这不会像您期望的那样工作,除非您添加一个单独的IO
包装器,其中包含不共享的runIO
函数(或属性,如果您喜欢)的行为用记忆结果覆盖自身,或者如果您添加某种方式来显式复制延迟操作。当然,无论如何只运行一次的效果——也就是说,它是为了使用效果来产生一个惰性/自引用的数据结构——所以这对你的目的来说可能没问题。Lazy
mfix f
f
抛开这些注意事项不谈,完全有可能为您的 thunk 结构表达类似于 的实例的东西MonadFix
,但这只是 的懒惰版本fix
:
// cf. Haskell ‘fix f = let ans = f ans in ans’
function lazyFix(f) {
var ans = Lazy(() => f(ans));
return ans;
}
如果您在这些 thunk 中交错了副作用,它们会在您强制它们时发生。
我认为MVar
原始 Haskell 代码中的业务主要只是捕获无限循环子集的一种便捷方式,通过确保如果动作在写入值之前尝试强制 thunk,那么运行时将能够检测到线程“在操作中被无限期阻塞MVar
”并引发错误。
作为旁注,您应该能够将您的lazyChain
定义简化为 just fm(mx.lazy)
,因为Lazy(() => fm(mx.lazy).lazy)
强制结果然后立即将其再次包装在一个 thunk 中,这既是额外的工作,也可能过于渴望您的交错效果。您的版本实际上是let fx = fm mx in mx `seq` fx `seq` fx
,可以简化(通过a `seq` a
= a
)为mx `seq` fm mx
,然后等效于fm $! mx
,即严格的函数应用程序。
推荐阅读
- excel - 单击工作表时触发工作表宏
- google-cloud-platform - GCP API 仪表板不计算我的所有请求
- python - 如果没有命令 python manage.py runcrons,Cron 不会执行我的任务
- vb.net - 两个 VSTO 加载项能否处理相同的 Outlook 应用程序事件 (application.newmailex)
- .net - .NET 正则表达式不正确匹配
- algorithm - 考虑到某些延误,在最坏的情况下到达目的地的最短时间?
- .net-core - Visual Studio 自定义项目模板 - applicationUrl 端口号
- linux - 对不同终端的命令
- angular - 如何使用 MatTabChangeEvent 获取内容
- regex - 使用 REGEX 在 Google Analytics 中为每个会话生成多次转化?