[MLton] OO in SML (was: mGTK and MLton)
Wesley W. Terpstra
terpstra@gkec.tu-darmstadt.de
Mon, 15 Nov 2004 16:01:01 +0100
--NzB8fVQJ5HfG6fxh
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
I was playing around with mGTK and wishing that it supported OO like
lablgtk2 when the following hack occured to me (see attached oo.sml).
With the addition of the '$' sign the syntax works pretty much like OCaml.
However, passing a method record around with every instance of the object
can't be the best way to do this; does anyone have a better idea?
I'd like to point out that using #foo to access member methods and variables
is not just motivated by OCaml. It was necessary because any other
formulation I could think of would require the structure in the name.
eg: o1&Object.increment () instead of o1&increment () as desired.
Furthermore, it seems to me that only record selectors are typeless enough
to be able to select completely different things depending on what the
object they are combined with.
See the type-safe example (test.sml) which mimics mGTK's type chaining.
Notice also in this code that the 'set' method has different arguments
depending on which object it gets applied to. =)
Suggestions for improvement?
--
Wesley W. Terpstra
--NzB8fVQJ5HfG6fxh
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="test.sml"
fun $ (obj, meths) sel = sel meths obj
signature OBJECT =
sig
type base
type 'a t
type constructor = unit -> int
val inherit : 'a -> constructor -> 'a t
val toStr : 'a t -> string
val new : unit -> base t
val newobj : unit -> (base t * {
toStr:'a t -> string })
end
structure Object :> OBJECT =
struct
type base = unit
type 'a t = int
type constructor = unit -> int
fun inherit x c = c ()
fun toStr ob = Int.toString ob
fun new () = 2
fun newobj () = (new (), { toStr=toStr })
end
signature SIGNAL =
sig
type base
type 'a signal_t
type 'a t = 'a signal_t Object.t
val inherit : 'a -> Object.constructor -> 'a t
val ping : 'a t -> string -> string
val new : unit -> base t
val newobj : unit -> (base t * {
toStr:'a t -> string,
ping:'a t -> string -> string })
end
structure Signal :> SIGNAL =
struct
type base = unit
type 'a signal_t = unit
type 'a t = 'a signal_t Object.t
fun inherit x c = Object.inherit () c
fun ping ob s = s ^ Object.toStr ob
fun new () = inherit () (fn () => 7)
fun newobj () = (new (), { toStr=Object.toStr, ping=ping })
end
signature FLAG =
sig
type base
type 'a flag_t
type 'a t = 'a flag_t Signal.t
val inherit : 'a -> Object.constructor -> 'a t
val set : 'a t -> string
val new : unit -> base t
val newobj : unit -> (base t * {
toStr:'a t -> string,
ping:'a t -> string -> string,
set:'a t -> string })
end
structure Flag :> FLAG =
struct
type base = unit
type 'a flag_t = unit
type 'a t = 'a flag_t Signal.t
fun inherit x c = Signal.inherit () c
fun set ob = Signal.ping ob "flag"
fun new () = inherit () (fn () => 42)
fun newobj () = (new (), { toStr=Object.toStr, ping=Signal.ping, set=set })
end
signature TOGGLE =
sig
type base
type 'a toggle_t
type 'a t = 'a toggle_t Signal.t
val inherit : 'a -> Object.constructor -> 'a t
val set : 'a t -> bool -> unit
val new : unit -> base t
val newobj : unit -> (base t * {
toStr:base t -> string,
ping:base t -> string -> string,
set:base t -> bool -> unit })
end
structure Toggle :> TOGGLE =
struct
type base = unit
type 'a toggle_t = unit
type 'a t = 'a toggle_t Signal.t
fun inherit x c = Signal.inherit () c
fun set ob v = ()
fun new () = inherit () (fn () => 87)
fun newobj () = (new (), { toStr=Object.toStr, ping=Signal.ping, set=set })
end
;
let
val ob = Object.newobj ()
val sg = Signal.newobj ()
val fl = Flag.newobj ()
val tg = Toggle.newobj ()
fun sgstuff x = $x#toStr ^ $x#ping "dsgf"
fun dostuff x = $x#toStr ^ $x#ping "das" ^ $x#set
(* check type safety: *)
val w = $sg#toStr
val x = $sg#ping "asf"
(*
val y = sg#set (* <-- won't compile :) *)
*)
val t = sgstuff fl (* sg,fl,tg all work, but not ob *)
val m = dostuff fl (* try ob, sg, and tg -- they won't work *)
(* note that here #set selects two different functions w/ different args *)
val z = $fl#set (* returning string -- use in print *)
val () = $tg#set false (* returning unit *)
val z2 = $tg#toStr
in
print (x ^ " " ^ z ^ " - " ^ m ^ "\n")
end ;;
--NzB8fVQJ5HfG6fxh
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="oo.sml"
fun $ (obj, meths) sel = sel meths obj
signature CLASS =
sig
type t
val newobj : int -> (t * {
(* "methods" *)
increment:t->unit->unit,
decrement:t->unit->unit,
stringify:t->unit->string,
(* "variables" *)
value:t->int
})
end
structure Object :> CLASS =
struct
type t = int ref
fun newobj i = (ref i, {
increment=fn (x:t) => fn () => x := !x + 1,
decrement=fn (x:t) => fn () => x := !x - 1,
stringify=fn (x:t) => fn () => Int.toString (!x),
value= fn (x:t) => !x })
end
val o1 = Object.newobj 3
val o2 = Object.newobj 6
val _ = (
print (Int.toString ($o1#value) ^ "\n");
$o1#increment ();
$o1#increment ();
print (Int.toString ($o1#value) ^ "\n");
if $o1#value = $o2#value
then print "bug\n"
else print "not equal\n";
$o2#decrement ();
if $o1#value = $o2#value
then print "equal\n"
else print "bug\n";
print ($o2#stringify () ^ "\n")
)
--NzB8fVQJ5HfG6fxh--