1 个不稳定版本
使用旧 Rust 2015
0.1.0 | 2020年8月25日 |
---|
#833 in 数学
55KB
1K SLoC
赫塞尔
在 Excel 中运行 Haskell 的插件。
动机
从某种意义上说,Excel 是一种函数式语言。单元格中的函数通常没有副作用。控制流表达式如 IF 都是函数,其他一切也是如此。然而,有些东西缺失了,对于这些,嵌入式真正函数式语言如 Haskell 非常有帮助。此外,由于 Excel 本身部分是函数式的,Haskell 以非常自然的方式嵌入,因此您可以在同一张工作表中轻松地同时使用 Excel 和 Haskell 函数。
- 在赫塞尔中,函数是一等对象,因此您可以在运行时创建一个函数,将其存储在 Excel 单元格中,并将其传递给其他函数。
- 在赫塞尔中,函数调用是非严格的,这使得处理无限递归函数和无限列表成为可能。函数只有在它们需要填充用户想要的单元格的程度时才会被评估,并且只有在需要时才会被评估。
- 赫塞尔允许您加载和编译 Haskell 模块,这为在电子表格中实现真正高性能计算打开了可能。它还允许您使用 Haskell 库,用于特定任务,如金融数学。
- 赫塞尔可选地使用浮点数,这些浮点数与本地 Excel 很好地接口,或者字符串,支持 Haskell 类型,如 Integer(无限制大小的整数)、Rational(有理数的精确表示)、Complex 等。
它是如何工作的
GHCI 是 Glasgow Haskell 的命令行解释器。从命令行可以执行 Haskell 代码,控制 Haskell 环境,加载/重新加载/卸载模块。可以将值、函数和其他数据结构分配给标签,这些标签可以在未来的命令行操作中使用。
此插件以单独的进程启动 GHCI,但控制其 stdin 和 stdout。这意味着我们可以在 Excel 中传递命令到 Haskell,分配和存储值、函数等,然后可以从其他单元格中使用它们。
如何使用它
关键函数是 hxAssign 和 hxShow。这些在 Haskell 中的等价表达式是 "a = foo x" 和 "foo x"。
您需要注意,Excel 本身也是一种函数式语言。单元格中的公式首先被检查以查找任何单元格之间的依赖关系,以便 Excel 可以生成一个循环图,类似于 Haskell 这样的函数式语言中的 AST。
与Haskell类似,表达式不是按照从左到右、从上到下的顺序进行评估,而是根据依赖图指定的顺序非严格地评估。
这意味着你必须小心地正确处理单元格之间的依赖关系。例如,如果你想使用使用hxAssign分配的变量,你需要确保在变量使用的地方之前,hxAssign单元格被评估。为了简化这个过程,像hxAssign这样的方法可以接受多个参数,这样你就可以指向定义你使用的变量的单元格。如果hxAssign成功,它会将其分配变量的名称写入单元格。
hxAssign
=hxAssign("add_three_cells", "{} + {} + {}", A1, A2, A3)
hxAssign允许你给Haskell表达式赋予一个名称。这个表达式可以是数字、字符串、列表或函数——任何Haskell可以赋予名称的东西。函数通常只有在标记为严格的情况下才会被评估。因此,可以合法地写出类似hxAssign("all_positive_integers", "[0..]")这样的代码。按照现在的样子,这可能会执行很长时间,但非严格评估意味着只有在需要时才会评估其所需的部分。
你可以使用hxAssign来定义函数,但定义必须是label = expression的形式。这意味着函数必须使用lambda表达式定义。(我们可能会在haxcel中添加语法糖方法,以支持更美观的函数定义。)
在成功的情况下,hxAssign返回名称(第一个参数),这使得以自然的方式链式依赖变得容易。注意,Excel不支持循环依赖结构,这与Haskell中的核心递归不同。在出现错误的情况下,它返回GHCI写入的任何错误信息,作为字符串。
hxAssign中的表达式(第二个参数)可以包含对其他变量的嵌入引用,这些引用指定为“{}”,类似于Rust或Python。最终,我们将支持大括号内的格式说明符,但尚未支持。这些引用第三个及以后的参数,这意味着你可以轻松地维护你的Haskell定义的依赖结构。
hxExec
=hxExec("add_three_cells + {} + {} + {}", B1, B2, B3)
hxExec允许你在Excel中显示Haskell计算的最终结果。如果计算简单地返回一个标量或字符串,这将是hxExec的返回值,并显示在单元格中。如果计算出现错误,错误信息将显示在单元格中。
与hxAssign类似,你可以在表达式中嵌入“{}”参数。
如果表达式返回一个列表或列表的列表,那么事情就变得有趣了。Haxcel会找出你试图填充公式的单元格数量,并在表达式上执行“take”,这样只执行所需的最小计算。
例如,你可以写
=hxExec("ints_from 0 where ints_from n = [n..] : (ints_from (n+1))")
这个表达式返回一个无限列表的无限列表。如果你在3x4单元格范围内将这个表达式作为数组公式(Ctrl+Shift+Enter)调用,它实际上会调用
hx_temp = ints_from 0 where ints_from n = [n..] : (ints_from (n+1))
:t hx_temp -- this finds that the expression is a list of lists
take 4 (map (take 3) hx_temp)
这使得在Haskell中调用永远不会返回的东西变得困难。尽管如此,你仍然需要小心——有些函数非常慢,无限列表的列表需要小心处理。
加载模块
这些函数是hxLoad和hxReload,分别对应GHCI中的“:l”和“:r”。当前目录由你的Excel设置定义,通常是“文档”目录,因此你可能需要提供要加载的Haskell文件的完整路径。
注意依赖关系。示例Excel文件test.xlsx显示了一种确保在调用它定义的函数之前加载模块的方法。
一些其他函数
函数 | 参数 | 行为 |
---|---|---|
hxShow | value, args... | 与hxExec相同,但总是写入字符串 |
hxRaw | command | 直接提交命令到GHCI并返回响应 |
hxVersion | 返回有关Haxcel的版本信息 | |
hxGHCIVersion | 返回 GHCI 的版本信息 | |
hxLoggingOn | 开启日志输出到 OutputDebugString(使用 DebugView 查看) | |
hxLoggingOff | 关闭日志 |
还有仅用于从 GHCI 读取或写入的功能,但应非常小心使用,因为它们可能会使 GHCI 处于不稳定状态,或者永久挂起。
依赖
~0.6–0.9MB
~12K SLoC