SML/NJ 无法覆盖其他模块中的函数
SML/NJ fail to override a function in other modules
我想覆盖 print
以让它输出到 stdout
以外的外流。我有两个文件:m1.sml
和 main.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,就像在所有现代编程语言中一样。
我想覆盖 print
以让它输出到 stdout
以外的外流。我有两个文件:m1.sml
和 main.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,就像在所有现代编程语言中一样。