#haskell #excel #string #list #execute #ghci #load

赫塞尔

赫塞尔:Excel 到 Haskell 的网关

1 个不稳定版本

使用旧 Rust 2015

0.1.0 2020年8月25日

#833 in 数学

MIT 许可证

55KB
1K SLoC

Rust 602 SLoC // 0.1% comments Haskell 468 SLoC // 0.0% comments

赫塞尔

在 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