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
Uh oh!
There was an error while loading. Please reload this page.
-
整体当前版本还是从 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