Mailing list for all users of the OCaml language and system.
 help / color / mirror / Atom feed
* Objects contrib
@ 1999-06-15 21:45 Fabrice Le Fessant
  1999-06-16  8:13 ` Vyskocil Vladimir
  1999-06-16 15:56 ` Objects contrib: new URL Fabrice Le Fessant
  0 siblings, 2 replies; 8+ messages in thread
From: Fabrice Le Fessant @ 1999-06-15 21:45 UTC (permalink / raw)
  To: caml-list


I have made a small patch to ocaml-2.02 to allow safe casts of objects.
The patch adds two new keywords "implements" and "cast". 

 - "implements" ("implements c1 c2") is used to declare that objects
from a class c1 can be cast to the type of another class
c2. Implements checks at compile time that a such cast is
safe. Polymorphic classes (class ['a] ...)  are not allowed as
"implements" parameters. However, derived classes are allowed 
(class b = [int] t). By default, casting objects to their original class
is always allowed without using "implements", but all other casts must be
precedeed by an "implements" which allows them.

- "cast" ("cast o1 c1") is used to cast an object o1 to the type of a class c1.
A runtime check is performed to verify that the original class of the object
can be cast to the new class (thanks to an "implements" instruction). If
not correct, a (Failure "Cast failure") is raised.

Here is an example of use. Such a cast is interesting to retrieve the
original type of an object after it has been subtyped to be stored in
a generic structure.

class t1 () =
  object 
  method a = 0
  end;;

class t2 () = 
  object
  method b = 1
  method a = 0
  end;;

let o1 = new t1 ();;
let o2 = new t2 ();; (* val o2 : t2 *)

let x = (o2 :> t1);; (* val x : t1 *)
let y = cast x t2;; (* cast x to its original class, val y : t2 *)

implements t2 t1;; (* t2 implements the interface of t1 *)
let x = (o2 :> < >);; (* val x: < > no methods *)
let y = cast x t1;; (* cast x to the interface of t1, val y : t1 *)

let z = cast o1 t2;;  (* ERROR: Failure "Cast failure" *)
implements t1 t2;;    (* ERROR: not (t1 :> t2) *)

This patch is available at:
http://pauillac.inria.fr/~lefessan/src/patch-cast.tar.gz

- Fabrice LE FESSANT

Homepage: http://pauillac.inria.fr/~lefessan




^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Objects contrib
  1999-06-15 21:45 Objects contrib Fabrice Le Fessant
@ 1999-06-16  8:13 ` Vyskocil Vladimir
  1999-06-16 15:56 ` Objects contrib: new URL Fabrice Le Fessant
  1 sibling, 0 replies; 8+ messages in thread
From: Vyskocil Vladimir @ 1999-06-16  8:13 UTC (permalink / raw)
  To: fessant; +Cc: caml-list

Fabrice Le Fessant wrote:
> 
> I have made a small patch to ocaml-2.02 to allow safe casts of objects.
> The patch adds two new keywords "implements" and "cast".

It sound very good to me, I needed such construct and for now I must
"tag"
objects with a sum constructor and "untag" them with pattern matching,
not
very convenient...
Will this patch be integrated in the official ocaml distribution ?

-- 
Vyskocil Vladimir
vyskocil@math.unice.fr
http://pcmath65.unice.fr/~vyskocil
http://www.inria.fr/safir/WHOSWHO/Vladimir.html




^ permalink raw reply	[flat|nested] 8+ messages in thread

* Objects contrib: new URL ...
  1999-06-15 21:45 Objects contrib Fabrice Le Fessant
  1999-06-16  8:13 ` Vyskocil Vladimir
@ 1999-06-16 15:56 ` Fabrice Le Fessant
  1999-06-17  2:26   ` Jacques GARRIGUE
  1 sibling, 1 reply; 8+ messages in thread
From: Fabrice Le Fessant @ 1999-06-16 15:56 UTC (permalink / raw)
  To: caml-list


Sorry, the previous link was false. 
The patch is available at:
http://pauillac.inria.fr/~lefessan/src/patch.cast.gz

----------------------------------------------------------------------

I have made a small patch to ocaml-2.02 to allow safe casts of objects.
The patch adds two new keywords "implements" and "cast". 

 - "implements" ("implements c1 c2") is used to declare that objects
from a class c1 can be cast to the type of another class
c2. Implements checks at compile time that such a cast is
safe. Polymorphic classes (class ['a] ...)  are not allowed as
"implements" parameters. However, derived classes are allowed 
(class b = [int] t). By default, casting objects to their original class
is always allowed without using "implements", but all other casts must be
precedeed by an "implements" which allows them.

- "cast" ("cast o1 c1") is used to cast an object o1 to the type of a class c1.
A runtime check is performed to verify that the original class of the object
can be cast to the new class (thanks to an "implements" instruction). If
not correct, a (Failure "Cast failure") is raised.

Here is an example of use. Such a cast is interesting to retrieve the
original type of an object after it has been subtyped to be stored in
a generic structure.

class t1 () =
  object 
  method a = 0
  end;;

class t2 () = 
  object
  method b = 1
  method a = 0
  end;;

let o1 = new t1 ();;
let o2 = new t2 ();; (* val o2 : t2 *)

let x = (o2 :> t1);; (* val x : t1 *)
let y = cast x t2;; (* cast x to its original class, val y : t2 *)

implements t2 t1;; (* t2 implements the interface of t1 *)
let x = (o2 :> < >);; (* val x: < > no methods *)
let y = cast x t1;; (* cast x to the interface of t1, val y : t1 *)

let z = cast o1 t2;;  (* ERROR: Failure "Cast failure" *)
implements t1 t2;;    (* ERROR: not (t1 :> t2) *)


- Fabrice

Homepage: http://pauillac.inria.fr/~lefessan




^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Objects contrib: new URL ...
  1999-06-16 15:56 ` Objects contrib: new URL Fabrice Le Fessant
@ 1999-06-17  2:26   ` Jacques GARRIGUE
  1999-06-17 17:16     ` Fabrice Le Fessant
  0 siblings, 1 reply; 8+ messages in thread
From: Jacques GARRIGUE @ 1999-06-17  2:26 UTC (permalink / raw)
  To: fessant; +Cc: caml-list

> I have made a small patch to ocaml-2.02 to allow safe casts of objects.
> The patch adds two new keywords "implements" and "cast". 

This looks both interesting and dangerous.

Interesting:
 Lots of people want such a feature. This is standard in
LISP or JAVA, so why not in Caml?
 A slight improvement: I would rather have cast raise Match_failure
than a standard Failure.  In particular with a match failure you have
position information for where it failed.

Dangerous:
 I believe this really goes against what ML is trying to be.
It allows people to write their programs like they would do in JAVA,
that is in a partially unsafe way. "I believe this object will have
type t, so let's cast it to t." What makes you believe so? I don't see
any guarantee for that.
 Another improvement for that would be to use a match construct rather
than only cast, and to have a warning if the match does not contain a
wild card at the end.

 Another problem is that it creates a distinction between classes with
parameters and classes without, which is not very natural.

 It would have been easy to introduce such a feature in the object
system from the beginning: caml objects bear a pointer to their class,
you just have to put the class name in the class.  My guess is that it
was intentionnally left out.

Remark:
 You can already implement something similar inside the language:

 For each class c create an hash-table referencing the object
 (In fact this should not be a standard hash-table, but a set of weak
  pointers, otherwise your objects will never be GCed as long as you
  don't remove them from the table)

	type empty_obj = < >
	let memo_c : (empty_obj, c) Hashtbl.t = Hashtbl.create 7

	let new_c args =
	  let obj = new c args in
	  Hashtbl.add memo_c (obj :> empty_obj) obj;
	  obj

	let cast_c obj = Hashtbl.find memo_c (obj :> empty_obj)

 Do not forget to catch Not_found when using cast_c !
 This works for parameterized classes also: you just have to create a
different memo for each parameters.
 You don't have the flexibility of the implements feature, but you
could also implement it by defining cast_c to look into the memos of
all subclasses in the implement hierarchy.

Regards,

	Jacques
---------------------------------------------------------------------------
Jacques Garrigue      Kyoto University     garrigue at kurims.kyoto-u.ac.jp
		<A HREF=http://wwwfun.kurims.kyoto-u.ac.jp/~garrigue/>JG</A>




^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Objects contrib: new URL ...
  1999-06-17  2:26   ` Jacques GARRIGUE
@ 1999-06-17 17:16     ` Fabrice Le Fessant
  1999-06-17 18:44       ` Stefan Monnier
  1999-06-18  1:19       ` Jacques GARRIGUE
  0 siblings, 2 replies; 8+ messages in thread
From: Fabrice Le Fessant @ 1999-06-17 17:16 UTC (permalink / raw)
  To: Jacques GARRIGUE; +Cc: fessant, caml-list


> A slight improvement: I would rather have cast raise Match_failure
> than a standard Failure.  In particular with a match failure you have
> position information for where it failed.

At the end of this message, you will find a patch to apply on
patch.cast to raise a Oo.CastFailure exception with the information
on the file and the line number where the exception was raised.

>  Lots of people want such a feature. This is standard in
> LISP or JAVA, so why not in Caml?

>  I believe this really goes against what ML is trying to be.

In every language, there is a compromise to do between expressiveness
and static verification of programs (by typing for example). I think
that a great quality of Objective-Caml is that it has reached a very
good compromise, and that each user can decide whether it is most
interested in the safety of the language -- for programing provers or
compilers for example -- or by the ease of programing in this language
-- for fast prototyping, or system programing. In both cases, he will
benefit from all the qualities of the language.

In our case, the "cast" construct for the object-oriented programing
style can be compared to the "ref" or the "array" types for the
imperative programming style. Both can lead to "unsafe" programs
(should the compiler allow "x.(-1)" in a program as it currently does?), 
but they can still be useful when used carefully. In that way, I trust
Ocaml users (demagogy :)).

With this patch, I tried to increase the expressiveness of the language,
without breaking its safety. In this way, I think it is still better
than using Obj.magic, since the "implements" checks whether the types
can correctly be coerced. 

> Remark:
> You can already implement something similar inside the language:

Can you really use an object (mutable) as the key in the hashtbl (its
hash value may change when it is modified ?) ?

Regards,

- Fabrice

*** tmp/patch.cast	Thu Jun 17 09:06:44 1999
--- tmp2/patch.cast	Thu Jun 17 09:05:52 1999
***************
*** 4,8 ****
  ***************
  *** 442,447 ****
! --- 442,450 ----
          event_after e (Lsend(Lvar met_id, transl_exp expr, []))
      | Texp_new (cl, _) ->
--- 4,8 ----
  ***************
  *** 442,447 ****
! --- 442,453 ----
          event_after e (Lsend(Lvar met_id, transl_exp expr, []))
      | Texp_new (cl, _) ->
***************
*** 10,14 ****
  +   | Texp_cast (exp, cl_path, cl_decl) ->
  +       Lapply(Translobj.oo_prim "class_cast",
! +         [transl_exp exp; transl_path cl_path])
      | Texp_instvar(path_self, path) ->
          Lprim(Parrayrefu Paddrarray, [transl_path path_self; transl_path path])
--- 10,17 ----
  +   | Texp_cast (exp, cl_path, cl_decl) ->
  +       Lapply(Translobj.oo_prim "class_cast",
! +         [transl_exp exp; transl_path cl_path;
! +         Lconst (Const_base(Const_string (
! +               Printf.sprintf "File %s: char %d" !Location.input_name
! +                 e.exp_loc.Location.loc_start)))])
      | Texp_instvar(path_self, path) ->
          Lprim(Parrayrefu Paddrarray, [transl_path path_self; transl_path path])
***************
*** 257,265 ****
  ***************
  *** 478,480 ****
! --- 487,500 ----
           done;
           print_newline ())
        (sort_buck !bucket_list)
! + 
  +   
  + let class_implements cl1 cl2 = 
--- 260,269 ----
  ***************
  *** 478,480 ****
! --- 487,501 ----
           done;
           print_newline ())
        (sort_buck !bucket_list)
! +
! + exception CastFailure of string
  +   
  + let class_implements cl1 cl2 = 
***************
*** 268,275 ****
  +   t1.casts <- t2 :: t1.casts
  +   
! + let class_cast o cl =
  +   let t1 = (magic (field (field o 0) 0): table) in
  +   let t2 = cl.table in
! +   if List.memq t2 t1.casts then o else failwith "Cast failure"
  diff -r -C 3 ocaml-2.02/stdlib/oo.mli csl/stdlib/oo.mli
  *** ocaml-2.02/stdlib/oo.mli	Sun Aug 16 23:28:41 1998
--- 272,279 ----
  +   t1.casts <- t2 :: t1.casts
  +   
! + let class_cast o cl location =
  +   let t1 = (magic (field (field o 0) 0): table) in
  +   let t2 = cl.table in
! +   if List.memq t2 t1.casts then o else raise (CastFailure location)
  diff -r -C 3 ocaml-2.02/stdlib/oo.mli csl/stdlib/oo.mli
  *** ocaml-2.02/stdlib/oo.mli	Sun Aug 16 23:28:41 1998
***************
*** 277,287 ****
  ***************
  *** 68,70 ****
! --- 68,73 ----
        distrib : int array; small_bucket_count: int; small_bucket_max: int }
    val stats: unit -> stats
    val show_buckets: unit -> unit
  + 
  + val class_implements : class_info -> class_info -> unit
! + val class_cast : Obj.t -> class_info -> Obj.t
  diff -r -C 3 ocaml-2.02/typing/typecore.ml csl/typing/typecore.ml
  *** ocaml-2.02/typing/typecore.ml	Fri Feb 12 13:34:32 1999
--- 281,292 ----
  ***************
  *** 68,70 ****
! --- 68,74 ----
        distrib : int array; small_bucket_count: int; small_bucket_max: int }
    val stats: unit -> stats
    val show_buckets: unit -> unit
  + 
+ + exception CastFailure of string
  + val class_implements : class_info -> class_info -> unit
! + val class_cast : Obj.t -> class_info -> string -> Obj.t
  diff -r -C 3 ocaml-2.02/typing/typecore.ml csl/typing/typecore.ml
  *** ocaml-2.02/typing/typecore.ml	Fri Feb 12 13:34:32 1999





^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Objects contrib: new URL ...
  1999-06-17 17:16     ` Fabrice Le Fessant
@ 1999-06-17 18:44       ` Stefan Monnier
  1999-06-18  1:19       ` Jacques GARRIGUE
  1 sibling, 0 replies; 8+ messages in thread
From: Stefan Monnier @ 1999-06-17 18:44 UTC (permalink / raw)
  To: caml-list

>>>>> "Fabrice" == Fabrice Le Fessant <fessant@pa.dec.com> writes:
> At the end of this message, you will find a patch to apply on
> patch.cast to raise a Oo.CastFailure exception with the information
> on the file and the line number where the exception was raised.

I have to agree with Jacques:  using a match construct (i.e. `typecase')
rather than a `cast' is cleaner.  It makes it a lot more obvious that the
cast might fail and in the case where you want to deal with several
alternatives, a `typecase' is exactly what you want, whereas using `cast'
would be very awkward (using handlers all over the place).
Of course a `cast' is a little better in the case where you *know* that
it will succeed, but the textual overhead of a `typecase' does not seem
significant.

> (should the compiler allow "x.(-1)" in a program as it currently does?),

No it shouldn't.  And type-systems are being developed that catch those uses
(pushing array bounds checking either to compile-time or (when the analysis is
not powerful enough) to the programmer (to insert the checks by hand)).


	Stefan




^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Objects contrib: new URL ...
  1999-06-17 17:16     ` Fabrice Le Fessant
  1999-06-17 18:44       ` Stefan Monnier
@ 1999-06-18  1:19       ` Jacques GARRIGUE
  1 sibling, 0 replies; 8+ messages in thread
From: Jacques GARRIGUE @ 1999-06-18  1:19 UTC (permalink / raw)
  To: fessant; +Cc: caml-list

From: Fabrice Le Fessant <fessant@pa.dec.com>

> In our case, the "cast" construct for the object-oriented programing
> style can be compared to the "ref" or the "array" types for the
> imperative programming style. Both can lead to "unsafe" programs
> (should the compiler allow "x.(-1)" in a program as it currently does?), 
> but they can still be useful when used carefully. In that way, I trust
> Ocaml users (demagogy :)).

Agreed for array indices. Looks like a "needed evil" since the only
way to avoid it would be dependent types (still, some people work at
it).

I don't see what is the problem with refs?  This is an impure
feature, but not dangerous in terms of type safety.

And of course there are also exceptions, which are pretty bad when
used the wrong way.

> With this patch, I tried to increase the expressiveness of the language,
> without breaking its safety. In this way, I think it is still better
> than using Obj.magic, since the "implements" checks whether the types
> can correctly be coerced. 

I agree that this is better than Obj.magic.  However it lets people
program in a JAVA-like style, quitting safe parametric polymorphism
(i.e. parametric classes) for unsafe dynamic type checking.

With your patch one can define collection classes without parameter,
put anything inside them, and downcast to get the contents, like one
does in JAVA.

The problem is that lots of people come to caml from a world where
casts are the standard way to obtain polymorphism.  If you provide it
with an easy syntax, they will not bother to learn about the benefits
of parametric polymorphism (anti-demagogy :)).

So I weaken my statement: casts are not an absolute evil which should
not be allowed at all, but at least they should be hard to use, enough
to encourage people to choose other methods when possible.

> > Remark:
> > You can already implement something similar inside the language:
> 
> Can you really use an object (mutable) as the key in the hashtbl (its
> hash value may change when it is modified ?) ?

Objects have a unique oid, which is used for comparisons and hashing.
Cf. byterun/hash.c.

I must admit that your approach is more efficient, since you just have
to check the pointer to the class. But again perfection may be a bad
thing for an unsafe feature...

Regards,

	Jacques

P.S. Thanks for your path-to-the-patch.




^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: Objects contrib: new URL ...
@ 1999-06-21 11:41 Don Syme
  0 siblings, 0 replies; 8+ messages in thread
From: Don Syme @ 1999-06-21 11:41 UTC (permalink / raw)
  To: 'Jacques GARRIGUE', fessant; +Cc: caml-list

> So I weaken my statement: casts are not an absolute evil which should
> not be allowed at all, but at least they should be hard to use, enough
> to encourage people to choose other methods when possible.

I'm not in favour of the extensive use of casts, but this 
sounds like the kind of reasoning that was behind SML's syntax for
references and imperative programming, which makes the imperative features
of SML very hard to use.  The problem is that when you really do need to use
casts in an
application, e.g. in window programming, then you'll probably need to use
them a fair bit,
so an awkward syntax could be quite problematic.

Don




^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~1999-06-21 17:41 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1999-06-15 21:45 Objects contrib Fabrice Le Fessant
1999-06-16  8:13 ` Vyskocil Vladimir
1999-06-16 15:56 ` Objects contrib: new URL Fabrice Le Fessant
1999-06-17  2:26   ` Jacques GARRIGUE
1999-06-17 17:16     ` Fabrice Le Fessant
1999-06-17 18:44       ` Stefan Monnier
1999-06-18  1:19       ` Jacques GARRIGUE
1999-06-21 11:41 Don Syme

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox