我想覆盖print
,让它输出到stdout
以外的输出。我在同一目录中有两个文件:m1.sml
和main.sml
。我想覆盖两个文件中print
的行为,但无法覆盖print
内的m1.sml
。
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
被覆盖。
我想知道为什么会这样?
答案 0 :(得分:2)
我想知道为什么会这样?
因为秩序。 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";
。另一种方法是使用所需的Demo
参数化print
模块:
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
那么,它有点傻,但如果它也做其他事情,让它参数化其副作用组件是非常有意义的,因为这意味着你可以更好地测试模块通过嘲笑那些。
编辑:在本地应用该仿函数似乎不属于标准ML的定义,因此它仅适用于莫斯科ML。 (我认为这与混合函数和模块的参数多态性有关。)我不确定是否有比在模块中包装testing
更漂亮的方法。
答案 1 :(得分:2)
因为ML中的标识符是lexically scoped,就像在所有现代编程语言中一样。