Replies: 2 comments 1 reply
-
全局状态不太清楚这里的全局状态实际上是跨线程的还是单线程的,如果是单线程可以使用thread_local。 函数闭包的实现Rust的枚举里面是可以定义结构体的, 完全可以写成: Fn {
name: String,
ns: String,
field: NanoId,
field: CalcitScope,
args: CalcitItems,
body: CalcitItems,
}, 或者你提到问题是不知道 函数传参, 引用和传值的问题使用引用有一个生命期(lifetime)的问题,有时候很难处理;直接使用owned变量clone来clone去又有很大的性能开销。你可以考虑用Arc/Rc包一层,引用计数也就相当于用了了最简单的GC了,当然也会有一定性能开销。还可以试试看Cow. Set/Map 顺序问题
后面的因为我也不太了解这个项目就不强答了。许多最佳实践可以看这个网站. |
Beta Was this translation helpful? Give feedback.
1 reply
-
Closure problems... https://zhauniarovich.com/post/2020/2020-12-closures-in-rust/ |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
整体当前版本还是从 calcit-runner.nim 照搬过来的.
原先的一些问题也照搬过来了, 很多没解决, 而且也带上来一些 Rust 的问题.
整理一下, 后续按照 TODO 做一下清理. 至于 Rust 相关的, 不确定是否有好的方案.
全局状态处理
全局状态在 JavaScript 在 Nim 里边不认为是什么问题, 但是在 Rust 就是个问题.
之前的 PureScript 版本, 用的是一个优点脏但也比较容易的方案解决的.
PureScript 本身运行在 JavaScript 当中也是单线程, 相比 Haskell 来说更不在乎这这一点.
Nim 版本中的行为类似 JavaScript, 直接允许(线程内的)全局变量:
https://github.com/calcit-lang/calcit_runner.rs/blob/e9f843b50141bbcdaa1b1df7038e2c278b0d288d/src/program.rs#L29-L31
这个在 Rust 不一样了, Rust 不允许直接定义可变的全局状态,
我学到的三种, Mutex, RWLock, Atomics, 我按照 lazy-static 那边抄过来就是 Mutex 了.
https://github.com/calcit-lang/calcit_runner.rs/blob/e9f843b50141bbcdaa1b1df7038e2c278b0d288d/src/program.rs#L179-L188
使用互斥锁就存在 dead lock 的可能, 之前我的经验比较少, 也不清相应的性能,
我现在弄明白的是部分的局部可变可以用 RefCell 处理,
https://github.com/calcit-lang/calcit_runner.rs/blob/0172e471c5b62f9d73548ac0c74cb413b4cd4e4d/src/codegen/emit_js.rs#L922
不过 RefCell 的局限也比较明显, 要一直传递引用, 在我的场景当中就是层层传递了.
我短期考虑还是往 RefCell 尝试. 但是不确定最好应该是怎样的方案.
函数闭包的实现
之前在 PureScript 里定义函数的时候, 发现可以做到比较简化的,
函数反正就用函数表示, 对应的 Nim 版本也是这样处理了...
https://github.com/calcit-lang/pure-run.purs/blob/dba4311f83ad649bc601bf98d3a19c05cb1f66b5/src/calcit/Primes.purs#L49
到了 Rust 我发现这样不行, 这个场景用到 Closure, 然后类型又是
FnMut
,我尝试了一下也不知道是不是真的不能那样处理, 总之类型不能通过,
最终我考虑还是简化到存储原始数据, 这样连闭包连 namespace 都要存储了,
https://github.com/calcit-lang/calcit_runner.rs/blob/0172e471c5b62f9d73548ac0c74cb413b4cd4e4d/src/primes.rs#L52-L59
更麻烦的是调用的时候, 程序内部函数, 跟代码定义的函数, 要分开处理,
结果就是这部分代码就很长, 而且要完整处理的话更不会短,
https://github.com/calcit-lang/calcit_runner.rs/blob/0172e471c5b62f9d73548ac0c74cb413b4cd4e4d/src/builtins/lists.rs#L239-L274
眼前这个问题还好不会扩散, 但是就不清晰, 不知道有没有方案可以进行简化.
或者
FnMut
在 Rust 当中究竟怎样使用, 还得再学学...函数传参, 引用和传值的问题
Rust 当中定义函数给了用指针的机会, 然后就大量出现了...
https://github.com/calcit-lang/calcit_runner.rs/blob/0172e471c5b62f9d73548ac0c74cb413b4cd4e4d/src/builtins/lists.rs#L283-L287
参考一些 std 的 API, 那些单查询用指针, 数据存储用 owned value, 还算清晰,
但是随着递归嵌套, 中间的怎么算? 我现在代码里大量用了传指针,
然后.. 又会出现后续的
.clone()
, 还有社区推荐的.to_owned()
.我现在能做到的是让编译器通过, 然而具体怎么回事, 细节怎样更合理, 就不好说了.
FP 语言, 也有这种数据引用和复制的概念, 但是借助 GC 的功能, 语言内部处理了这问题,
我现在相当于强行用 Rust 模拟我在 immer 里的理解, 特别是使用 im 的场景.
可能就是需要更多 Rust 经验吧.
Set/Map 顺序问题, 类似还有判断
Set 跟 Map 本身认为是无序的, 单纯使用的时候也是无序就够了,
但是定义数据用于程序内部的场景, 比如 Hash, Ordering 这些 trait 就需要了,
也不需要一个有用的顺序, 但是在生命周期内稳定. 或者就是现在这样不可用的状态.
"不可用"意味着 HashMap 不可作为 HashMap 的 key...
https://github.com/calcit-lang/calcit_runner.rs/blob/0172e471c5b62f9d73548ac0c74cb413b4cd4e4d/src/primes.rs#L335-L340
https://github.com/calcit-lang/calcit_runner.rs/blob/0172e471c5b62f9d73548ac0c74cb413b4cd4e4d/src/primes.rs#L241-L247
对于暴露给外部的 API 来说,
first
函数会需要处理 Set 和 Map 的场景,因为我毕竟是需要在程序当中遍历处理 Set 和 Map 的元素的...
目前只是按照 Rust 自带的
.iter()
提供了一个实现,https://github.com/calcit-lang/calcit_runner.rs/blob/0172e471c5b62f9d73548ac0c74cb413b4cd4e4d/src/builtins/lists.rs#L354-L359
同样, 我不能理解清除后续可能造成什么奇怪的问题.
单纯对于展示和遍历处理, 还好 Set 在处理后还是不在乎顺序的.. 那暂且无所谓.
生成代码, 用于优化性能的缓存
calcit-runner 内置有一个功能是 watch 更新文件
.compact-inc.cirru
替换程序运行时.在代码层面是可以直接替换的, 但是程序运行的数据, 未必能替换掉, 有些极端的情况,
比如更新后不再使用 lib 当中某个函数, 那么函数对应的数据是应该清空掉的,
比如当前 package 内, 某个 macro 变化了, 那么其他文档虽然不变, 代码还是可能改变的.
现在我也没打算做精确的依赖追踪(信息应该是足够), 无法准确知道, 就只能先选择全部清除,
https://github.com/calcit-lang/calcit_runner.rs/blob/0172e471c5b62f9d73548ac0c74cb413b4cd4e4d/src/program.rs#L219-L225
此前 Nim 版本用的方案是清除当前 package 包含的所有命名空间, 基本够用的,
但是似乎出现过生成 js 以后代码不对应的问题, 开发环境跟最终打包不对应..? 记不清了.
所以现在不放心只清空当前 package. 似乎应该先加上, 但是准备好捕捉具体问题?
可能近期是要加一下.
后续问题
之前的 calcit-js 缺失的功能还是挺多的, 比如 async/await 现在就用不了, 只能引入 js 文件去用.
不过这种, 主要还是设计语法上, 比较会出现 Rust runtime 跟 js runtime 不一致, 尽量得小点.
另外的问题还是我心心念念的图形库啊, 难道再接一个 Cario 来做简单的图案吗..
或者有没有更省事但是又更强大的方案呢, 甚至通过脚本来处理 3D 图案?
现在已有的是基于 PIXI.js 封装的 phlox 可以画画 2D 图案, 暂时够用,
不过 PIXI 也是很耗内存的, 而且我不知道怎么优化.. 都是问题.
总之没有好的方案方案, calcit-runner 的实用性还是弱很多. 没想明白... 再看吧.
Beta Was this translation helpful? Give feedback.
All reactions