扩展 ts-Wolfram:放弃面向对象编程,内核/用户空间互操作,改进打印
Extending ts-Wolfram: dropping OOP, kernel/userspace interop, better printing

原始链接: https://www.spakhm.com/ts-wolfram-ext

## ts-wolfram 更新:更简单、更灵活、更易读 ts-wolfram 解释器的最新更新着重于改进代码结构和功能。最初的面向对象方法已被代数数据类型取代,从而使代码更简洁,并消除了繁琐的 `instanceof` 检查。这种转变符合首选的基于管道的编程风格。 一项关键改进允许在表达式求值期间,TypeScript(“用户空间”)和 Mathematica(“内核”)代码之间实现互操作性。这使得通过“序言”规则进行便捷的转换成为可能,例如将 `Times[Minus[a], Minus[b]]` 简化为 `Times[a, b]`。 最后,打印功能得到了增强。虽然最初的用户空间美化打印计划因不希望的求值而存在问题,但现在基于 TypeScript 的解决方案为诸如 `Hold[a /. a->b]` 之类的表达式提供了简短的输出,以及用于详细调试的 `FullForm` 命令。还需要进一步的工作来解决复杂的打印场景,例如在 `Sin[Cos[x]] Sin[x]` 之类的表达式中优化括号删除。

Hacker News 新闻 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 扩展 ts-Wolfram:放弃面向对象编程,内核/用户空间互操作,更好的打印 (spakhm.com) 3 分,作者 lioeters 1小时前 | 隐藏 | 过去 | 收藏 | 1 条评论 dvh 10分钟前 [–] 2024年10月 回复 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文
Extending ts-wolfram: dropping OOP, kernel/userspace interop, better printing - Slava Akhmechet

I mentioned before that getting an interpreter working is a nerd-snipe. Once it works a little, it’s so much fun to keep adding functionality you end up working on it despite yourself. In this spirit, here is a writeup about my latest changes to ts-wolfram.

Dropping OOP

I initially wrote the interpreter using OOP. There were three AST types (Integer, Symbol, and Form), each one derived from a Node interface. This worked but bothered me, primarily as an aesthetic matter. I prefer to think of programs in terms of pipelines of transformations between data types. You can do this with classes, but stylistically it doesn’t quite fit and tends to leave a feeling of dissatisfaction. So I got rid of classes in favor of an algebraic data type, which to me looks much nicer:

export type Expr = Integer | Symbol | Form | String;

export enum Types {
  Form, Integer, Symbol, String,
}

export type Form = {
  type: Types.Form,
  head: Expr,
  parts: Expr[],
}

export type Integer = {
  type: Types.Integer,
  val: number,
}

Many small changes downstream of dropping OOP also made the code much nicer. For example, I got rid of ugly instanceof calls. But overall, code organization could still use one more pass of deliberate thinking through the file structure, and putting code that belongs together in dedicated files.

Kernel/userspace interop

Second, I changed the interpreter to support mixing kernel” and userspace” code in expression evaluation. Previously a function could either be defined in Typescript, or in Mathematica code, but not both. For example, Times is defined in Typescript, which meant I couldn’t put convenient transformation code into the prelude. Typing -a (-b) in the REPL produced Times[Minus[a], Minus[b]]. It would be convenient to add a rule to transform this into Times[a, b], but the evaluator didn’t support that.

Supporting this ended up being a small change. Once the evaluator supported mixed definitions I added the following line to the prelude:

Times[Minus[x_], Minus[y_]] := Times[x, y];

And voilà! Typing -a (-b) now produces Times[a, b] without any changes to Typescript code.

Better printing

Finally, I wanted to improve printing. Typing something like Hold[a /. a->b] printed the full form Hold[ReplaceAll[a, Rule[a, b]]]. I wanted to print shorthand. My original plan was to add string support and then use the same trick as with Times[Minus[...], Minus[...]] to do pretty printing in userspace. I added strings, ToString and <>/StringJoin, but then realized adding userspace rules to ToString doesn’t quite work. Calling ToString from the interpreter to pretty print caused additional evaluation and printed incorrect results.

Instead of going against the grain and trying to get this to work, I just kept the string commands and implemented pretty printing in Typescript. I added support for basic syntax so expressions like Hold[a /. a->b] print correctly in shorthand. I also added the FullForm command to help with debugging when I do need to see the full form.

However, getting a good printer working is non-trivial. For example, Sin[Cos[x]] Sin[x] currently prints (Sin[Cos[x]]) (Sin[x])– the printer doesn’t know to drop parentheses. I didn’t look into this too closely, but it seems like the naive approach would require lots of special cases to print expressions nicely. To get the printer working well, I need either to add many of the special cases, or find a more general approach (assuming one exists).

Oct 26, 2024

联系我们 contact @ memedata.com