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

时间:2018-03-12 07:44:01

标签: sml smlnj

我想覆盖print,让它输出到stdout以外的输出。我在同一目录中有两个文件:m1.smlmain.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被覆盖。

我想知道为什么会这样?

2 个答案:

答案 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,就像在所有现代编程语言中一样。