【问题标题】:How to convert anything to string, in SML?如何在 SML 中将任何内容转换为字符串?
【发布时间】:2013-10-21 01:03:56
【问题描述】:

我正在尝试实现一个测试函数来比较并在它们不相等时显示错误消息:

exception AssertionErrorException of string

fun assert(testName, actual, expect) : bool =
    if actual = expect
    then true
    else raise (AssertionErrorException (testName ^ " failed. actual: " ^ actual 
                ^ ", expect: " ^ expect ));

不幸的是,如果我用非字符串参数调用它,它就不起作用:

assert("test1", SOME [], NONE);

无法编译,报错信息为:

Error: operator and operand don't agree [tycon mismatch]
  operator domain: string * string * string
  operand:         string * 'Z list option * 'Y option
  in expression:
    assert ("test1",SOME nil,NONE)

如何解决?

【问题讨论】:

    标签: sml polyml


    【解决方案1】:
    structure Printf =
       struct
          fun $ (_, f) = f (fn p => p ()) ignore
          fun fprintf out f = f (out, id)
          val printf = fn z => fprintf TextIO.stdOut z
          fun one ((out, f), make) g =
             g (out, fn r =>
                f (fn p =>
                   make (fn s =>
                         r (fn () => (p (); TextIO.output (out, s))))))
          fun ` x s = one (x, fn f => f s)
          fun spec to x = one (x, fn f => f o to)
          val B = fn z => spec Bool.toString z
          val I = fn z => spec Int.toString z
          val R = fn z => spec Real.toString z
       end
    

    这是一个使用示例。

    val () = printf `"Int="I`"  Bool="B`"  Real="R`"\n" $ 1 false 2.0
    

    这将打印以下内容。

    Int=1  Bool=false  Real=2.0
    

    更多信息请看here

    【讨论】:

    • 谢谢,但我不想打印出来,只想返回一个可以在错误消息中使用的字符串
    • 也许不是问题的答案,但绝对很有帮助。一个小提示:要使其编译,您必须在某处提供标识函数:“fun id x = x”
    【解决方案2】:

    在 Haskell 中,您可以让您的类型成为类型类 Show 的实例,并实现函数 show :: Show a => a -> String 的重载变体,然后打印 show x 而不是 x。不幸的是,标准 ML 中不存在这样的类型类,因此您不得不为要打印的每种数据类型编写自己的 show 的非重载变体。

    一些 SML 编译器(至少莫斯科 ML)支持重载函数 makestring,它仅适用于内置类型的子集,不适用于任何复合类型。例如。 makestring 2makestring 2.0 都有效,但 makestring (0,0) 无效。 编辑:David Matthews 在下面的回答中指出 PolyML 中的 makestring 更好。)

    如果您希望创建一个通用的断言函数来漂亮地打印错误,您可以做的一件事是为您希望断言其值的每种类型创建一个带有构造函数的数据类型。这将像 C 中的“联合”类型一样工作。

    exception AssertionError of string
    datatype assert = AssertInt of int
                    | AssertReal of real
                    | AssertBoolBool of bool * bool
                    | ...
    
    fun assertPP (AssertInt i) = Int.toString i
      | assertPP (AssertReal r) = Real.toString r
      | assertPP (AssertBoolBool (b1,b2)) =
        String.concat ["(", Bool.toString b1, ", ", Bool.toString b2, ")" ]
      | assertPP (...) = ...
    
    fun assert (testName, actual: assert, expect: assert) =
        actual = expect  (* ML infers equality for constructors *)
        orelse raise AssertionError (String.concat
            [ testName, " failed. actual: ", assertPP actual,
              ", expect: ", assertPP expect, "." ])
    

    这是一个穷人的超载替代品。

    【讨论】:

      【解决方案3】:

      makestring 出现在标准 ML 的一些早期草案中,但在最终版本之前被删除。 Poly/ML 将其保留为 PolyML.makestring,这适用于任何类型,包括结构化类型。

      通过这个特殊的例子,可以写

      fun assert(testName, actual, expect) =
      if actual = expect
         then true
         else raise AssertionErrorException(testName ^ " failed. actual: " ^
                      PolyML.makestring actual ^ ", expect: " ^
                      PolyML.makestring expect);
      

      所以

       assert("test1", SOME [], NONE);
      

      打印

      Exception-
      AssertionErrorException "test1 failed. actual: SOME [], expect: NONE"
         raised
      

      这恰好起作用,因为 actualexpect 的类型是相等类型,这为编译器提供了足够的信息来正确打印值。不过,一般来说,如果 PolyML.makestring 包含在多态函数中,那么所有将打印的都是“?”。解决方案是传入一个额外的参数,该参数是一个将特定类型转换为字符串的函数。

      fun assert(testName, actual, expect, toString) =
         if actual = expect
         then true
         else raise AssertionErrorException(testName ^ " failed. actual: " ^
                      toString actual ^ ", expect: " ^ toString expect );
      

      然后您需要传入一个将特定值转换为字符串的函数。在 Poly/ML 中,这可以是 PolyML.makestring

      assert("test2", (1,2,3), (1,2,4), PolyML.makestring);
      

      打印

      Exception-
         AssertionErrorException
        "test2 failed. actual: (1, 2, 3), expect: (1, 2, 4)" raised
      

      如果您使用不同的 SML 实现,您仍然可以执行相同的操作并为特定类型传递您自己的转换函数。

      assert("test2", (1,2,3), (1,2,4),
           fn (a,b,c) =>
              String.concat["(", Int.toString a, ",", Int.toString b,
                            ",", Int.toString c, ")"]);
      

      实际上,您正在实现上一个答案中描述的类型类。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-17
        相关资源
        最近更新 更多