SML/NJ 无法覆盖其他模块中的函数

SML/NJ fail to override a function in other modules

我想覆盖 print 以让它输出到 stdout 以外的外流。我有两个文件:m1.smlmain.sml 在同一目录中。我想覆盖两个文件中 print 的行为,但无法覆盖 m1.sml.

中的 print

m1.sml

structure Demo : sig
  val printSomething : string -> unit
  end = 
  struct
    fun printSomething s = print s
  end

main.sml

use "m1.sml";

fun redirectPrint strm str = TextIO.output (strm, str);

fun testing output = 
  let 
      val outStrm = TextIO.openOut output
      val print = redirectPrint outStrm 
  in
    (print "This will show up in test.txt\n"; 
     Demo.printSomething "This won't show up in test.txt\n"; 
     TextIO.closeOut outStrm)
  end;

testing "test.txt"

运行 sml main.sml 将产生

...
[autoloading done]
val redirectPrint = fn : TextIO.outstream -> TextIO.vector -> unit
val testing = fn : string -> unit
this won't show up in test.txt
val it = () : unit

如您在输出中所见,它适用于行

print "This will show up in test.txt\n" (written to test.txt)

但不是下一行

Demo.printSomething "This won't show up in test.txt\n"; (printed to stdout)

只打印到 stdout

然而,

Demo.printSomething 也会调用 print。此外,在调用 Demo.printSomething 的当前作用域中,print 被覆盖。

我想知道为什么会这样?

I wonder why this happens?

因为顺序。 Demo.printSomething指的是旧的print。当您稍后在本​​地范围内重新绑定 print 时,Demo.printSomething 不会更改其定义。一个更简单的例子:

- val y = 42;
> val y = 42 : int
- fun f () = y;
> val f = fn : unit -> int
- val y = 43;
> val y = 43 : int
- f ();
> val it = 42 : int

一种似乎不起作用的方法可能是在重新定义后在本地范围内 ;use "m1.sml";。另一种方法是使用所需的 print:

参数化 Demo 模块
functor Demo (P : sig val print : string -> unit end) =
struct
  val printSomething = P.print
end

然后使用修改后的打印功能将其应用于您的本地范围:

;use "m2.sml";

fun curry f x y = f (x, y)

fun testing output =
    let
      val outStrm = TextIO.openOut output
      val print = curry TextIO.output outStrm
      structure ThisDemo = Demo(struct val print = print end)
    in print "foo\n"
     ; ThisDemo.printSomething "bar\n"
     ; TextIO.closeOut outStrm
    end;

val _ = testing "test.txt"

当然,如果这就是 Demo 所做的一切,那就有点傻了,但如果它还做其他事情,让它参数化它的 side-effectful 组件是非常有意义的,因为这意味着你可以通过模拟那些更好地测试模块。

编辑: 在本地应用该仿函数似乎不是标准 ML 定义的一部分,因此它仅适用于 Moscow ML。 (我认为这与混合函数和模块的参数多态性有关。)我不确定是否有比将 testing 包装在模块中更漂亮的方法。

因为 ML 中的标识符是 lexically scoped,就像在所有现代编程语言中一样。