Skip to content

Instantly share code, notes, and snippets.

@m2ym
Created April 8, 2015 09:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save m2ym/70475023c03e6d298449 to your computer and use it in GitHub Desktop.
Save m2ym/70475023c03e6d298449 to your computer and use it in GitHub Desktop.
Try to implement Haskell's show in OCaml using ppx_overload
module type Show = sig
val show : 'a -> string
end
module Show = struct
external show : 'a -> string = "%OVERLOADED"
module Int = struct
let show = string_of_int
end
module Float = struct
let show = string_of_float
end
(* too ambiguous *)
module Tuple2 = struct
let show (a, b) = show a ^ ", " ^ show b
end
(* functor? *)
module Tuple2 (A : Show) (B : Show) = struct
let show (a, b) = A.show a ^ ", " ^ B.show b
end
end
@camlspotter
Copy link

突き進んでいいのかという問題は横において、二つ目の Tuple2 は、

module Tuple2 : functor (A : Show) (B : Show) -> sig val show : 'a * 'b -> string end

という型を持っていますが、 'aA'bB の関連性がないのでトランスレータは何をしたらいいかわからないですね。逆にそれがわかると、実装は大変かもしれないができるようなきがします。

module Show = struct
  (* class Show 'a のようなもの *)
  module type Show = sig
    type a
    val show : a -> string
  end

  (* 上から自動生成可能に見える *)
  external show : 'a -> string = "%OVERLOADED"

  (* Instance Show int のようなもの   (モジュール型は推論されますね) *)
  module Int : Show with type a = int = struct
    type a = int
    let show = string_of_int
  end

  (* Instance Show float のようなもの   (モジュール型は推論されますね) *)
  module Float : S with type a = float = struct
    type a = float
    let show = string_of_float
  end

  (* 他のモジュールに依存したファンクタ。例によってモジュール型は推論されるはず 
      instance Show 'a -> Show 'b -> Show ('a * 'b) のようなもの
  *)
  module Tuple(A : Show)(B : Show) : Show with type a = A.a * B.a = struct
    type a = A.a * B.a
    let show (a,b) = A.show a ^ ", " ^ B.show b
  end
end

こんな感じだとうまくできるようなきがします。

show (1, 2.3)let X = Tuple(Int)(Float) in X.show (1, 2.3) になりますけど、同じ型のインスタンスをいちいち functor application していると勿体無いので本気でやるなら、 contraction する必要がありますねえ

@m2ym
Copy link
Author

m2ym commented Apr 8, 2015

なるほど、 with type a = A.a * B.a でうまく関連できるのですね。ぜんぜん分かっていませんでした。 show (1, 2.3)

let x : ('a, 'b) = (1, 2.3) in
let module M =
  Tuple
    (struct
       type t = 'a
       let show x = Show.show x
     end)
    (struct
       type t = 'b
       let show x = Show.show x
     end)
in M.show x

みたいにできれば勝利ですが、外部から 'a'b は入れられないんですよね。 (type a) とかで型パラメータをうまく導入してやればいけるかもしれません。もう少し考えてみます。

@camlspotter
Copy link

ちょっと違う件ですが、 ppx_overload だけでもこんなの書けます

module type Num = sig 
  type a
  val (+) : a -> a -> a
end

module Num = struct

  external (+) : 'a -> 'a -> 'a = "%OVERLOADED"

  module Int = struct
    type a = int
    let (+) = Pervasives.(+)
  end

  module Float = struct
    type a = float
    let (+) = Pervasives.(+.)
  end

end

let () = assert Num.(1.2 + 3.4 = 4.6)

(* Trial of double : fails due to "too ambiguous" *)
(* let double x = Num.(x + x) *)

let double (type a) a (x : a) = 
  (* Extend Num with a *)
  let module Num = struct
    include Num
    module A = (val a : Num with type a = a)
  end in
  Num.(x + x) (* No need to tell which (+) we should use. Nice! *)

(* We need explicit application of the instance of Num float... *)
let () = assert (double (module Num.Float) 1.2 = 2.4)

@m2ym
Copy link
Author

m2ym commented Apr 9, 2015

なるほど。参考になります。

Copy link

ghost commented May 6, 2015

Why not use english:(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment