What is it, naokirin?

OUnitで遊ぶ (2)

前回とはうって変わって、ちょっとだけassert文をどうにか出来ないかなぁという感じの方向性で作ってみました。

module type ASSERT = sig
  type t
  val cmp : (t -> t -> bool)
  val printer : (t -> string)
end

module MakeAssert(AssertType:ASSERT) : sig
  type t = AssertType.t
  val assert_equal : string -> t -> t -> unit
  val (@=) : string -> t * t -> unit
end = struct
  type t = AssertType.t
  let assert_equal msg expected actual =
    OUnit.assert_equal ~cmp:AssertType.cmp ~printer:AssertType.printer
      ~msg:msg expected actual
  let (@=) msg (expected, actual) = assert_equal msg expected actual
end

module AssertInt = MakeAssert(struct
  type t = int
  let cmp = (=)
  let printer = string_of_int
end)

module AssertString = MakeAssert(struct
  type t = string
  let cmp = (=)
  let printer s = s
end)

module AssertFloat = MakeAssert(struct
  type t = float
  let cmp = (=)
  let printer = string_of_float
end)

let (@!) msg (e, f) = OUnit.assert_raises ~msg:msg e f

MakeAssertというファンクタを用いて、様々な型に対応するassert_* を作ってしまおうと言う感じで作ってみました。
正直、作ってみたところ、OUnitのassert_* で型に依存するような実装になっているのがassert_equalしかなかったので微妙でした。

あとはassert_boolに対応する (@?) のような書き方をassert_equalやassert_raisesで出来ないか試してみています。
ファンクタ内にパラメタライズドテストが簡単にできるようなヘルパ関数を用意してあげるとさらに便利になるかなと思いますが、それでも現状のOUnitに比べてそれほど利便性が向上したり、可読性が良くなったりするような感じではないですね。。。

個人的には久々にファンクタを使ったり、First class moduleを試してみたりしていたので満足です^^

追記:
そういえば、First class moduleとか、type variableにexplicit に名前を付けられるようになったとかが3.12にあるので、それつかってみようかなと。

let ex_assert_equal (type s) (cmp : s -> s -> bool) (printer : s -> string) =
  let module Ass = MakeAssert(struct type t = s
    let cmp = cmp
    let printer = printer
  end) in
  Ass.assert_equal

書いてみたけど、あんまりありがたみはない気がする。ただこっちの方がassert_equalしか恩恵がないところを考えると、モジュール一個作るよりはいい感じかな。正直assert_equalでしかここでのコードの恩恵がない事を考慮すると、関数で対処するのが最善だし使うならこっちかな。