caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] Questions about default instances
@ 2011-08-04 16:22 Sébastien Furic
  2011-08-08  7:14 ` Jacques Garrigue
  0 siblings, 1 reply; 3+ messages in thread
From: Sébastien Furic @ 2011-08-04 16:22 UTC (permalink / raw)
  To: caml-list

  Hello,

  What is the usual way in OCaml to define mutually recursive classes 
that share default instances? In order to illustrate my problem, let's 
suppose we would like to define a hierarchy of classes defining booleans 
/à la/ Smalltalk:

-8<-------------------------------------------------------------

class virtual object_ = object
   method virtual to_string: string
end

class virtual boolean = object
   inherit object_
   method virtual not_: boolean
   method virtual or_: boolean Lazy.t -> boolean
   method virtual and_: boolean Lazy.t -> boolean
   method virtual if_: 'a . 'a Lazy.t -> 'a Lazy.t -> 'a
end

class false_class = object (self)
   inherit boolean
   method to_string = "false"
   method not_ = new true_class
   method or_ chk = Lazy.force chk
   method and_ _ = (self :> boolean)
   method if_ _ chk = Lazy.force chk
end

and true_class = object (self)
   inherit boolean
   method to_string = "true"
   method not_ = new false_class
   method or_ _ = (self :> boolean)
   method and_ chk = Lazy.force chk
   method if_ chk _ = Lazy.force chk
end

(* Default instances (that I would have preferred to inject
    directly in definitions above) *)
let false_ = new false_class
and true_ = new true_class
-8<-------------------------------------------------------------

  Methods corresponding to message not_ in both false_class and 
true_class should ideally return a instance of, respectively, true_class 
and false_class that would have been created once and for all (instead 
of a fresh instance each time they are invoked). How does one achieve 
this in OCaml the functional way? (i.e., without resorting to 
references, but also, ideally, without resorting to lazy values)
  The use of immediate objects is IMO a better choice to implement 
booleans (because I don't want nor need to let users subclass 
false_class and true_class). Here is an attempt:

-8<-------------------------------------------------------------
(* Using the same definition of boolean above *)
let rec false_ = object (self)
   inherit boolean
   method to_string = "false"
   method not_ = true_
   method or_ chk = Lazy.force chk
   method and_ _ = self
   method if_ _ chk = Lazy.force chk
end

and true_ = object (self)
   inherit boolean
   method to_string = "true"
   method not_ = false_
   method or_ _ = self
   method and_ chk = Lazy.force chk
   method if_ chk _ = Lazy.force chk
end
-8<-------------------------------------------------------------

  I get “Error: This kind of expression is not allowed as right-hand 
side of `let rec'”. I wonder why OCaml does not accept the definition of 
recursive values like above (notice that references to false_ and true_ 
are “protected” by method definitions). Wouldn't it be safe to extend 
recursive definitions with this pattern?

  Cheers,

  Sébastien.

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

* Re: [Caml-list] Questions about default instances
  2011-08-04 16:22 [Caml-list] Questions about default instances Sébastien Furic
@ 2011-08-08  7:14 ` Jacques Garrigue
  2011-08-16  8:42   ` Sébastien Furic
  0 siblings, 1 reply; 3+ messages in thread
From: Jacques Garrigue @ 2011-08-08  7:14 UTC (permalink / raw)
  To: Sébastien Furic; +Cc: caml-list

On 2011/08/05, at 1:22, Sébastien Furic wrote:

> What is the usual way in OCaml to define mutually recursive classes that share default instances?

There is no concept of "default instance" in OCaml, and as you discovered yourself,
default instances create problems with initialization.
Namely, an object constructor might attempt to access a default instance before it is built,
so we need a way to know whether it is ready or not.
This is not specific to OCaml.
But since OCaml has no null pointers (with good reasons!), the only way to know whether
a value is ready or not is to use either an option or a a lazy value.

So a first solution to your problem is (using immediate objects):

class type object_ = object
 method to_string: string
end

class type boolean = object
 inherit object_
 method not_: boolean
 method or_: boolean Lazy.t -> boolean
 method and_: boolean Lazy.t -> boolean
 method if_: 'a . 'a Lazy.t -> 'a Lazy.t -> 'a
end

let rec lazy_false = lazy (object (self : #boolean)
 method to_string = "false"
 method not_ = Lazy.force lazy_true
 method or_ chk = Lazy.force chk
 method and_ _ = self
 method if_ _ chk = Lazy.force chk
end)

and lazy_true = lazy (object (self : #boolean)
 method to_string = "true"
 method not_ = Lazy.force lazy_false
 method or_ _ = self
 method and_ chk = Lazy.force chk
 method if_ chk _ = Lazy.force chk
end)

let false_ = Lazy.force lazy_false
and true_ = Lazy.force lazy_true

Note that I used class types rather than virtual classes, since there is no point
in a virtual class having no concrete methods.
If you want a virtual class later, you can even build it from the class type:

class virtual boolean_class = object (self : #boolean)
  method as_bool = self#if_ (lazy true) (lazy false)
end

Now, if you want to use classes instead of immediate objects, there is an additional problem:
a class can only recurse with other classes.
A simple way out is to use recursive modules.

module rec Bool : sig
  class false_class : boolean
  class true_class : boolean
  val true_ : boolean Lazy.t
  val false_ : boolean Lazy.t
end = struct
  class false_class = object (self : #boolean)
    method to_string = "false"
    method not_ = Lazy.force Bool.true_
    method or_ chk = Lazy.force chk
    method and_ _ = (self :> boolean)
    method if_ _ chk = Lazy.force chk
  end

  and true_class = object (self : #boolean)
    method to_string = "true"
    method not_ = Lazy.force Bool.false_
    method or_ _ = (self :> boolean)
    method and_ chk = Lazy.force chk
    method if_ chk _ = Lazy.force chk
  end

  (* Default instances (that I would have preferred to inject
     directly in definitions above) *)
  let false_ = lazy (new false_class)
  and true_ = lazy (new true_class)
end

Recursive modules do not remove the need for laziness:
for exactly the same reason, all recursion must go through
a module containing only functions and lazy values.

Jacques Garrigue


> In order to illustrate my problem, let's suppose we would like to define a hierarchy of classes defining booleans /à la/ Smalltalk:
> 
> -8<-------------------------------------------------------------
> 
> class virtual object_ = object
>  method virtual to_string: string
> end
> 
> class virtual boolean = object
>  inherit object_
>  method virtual not_: boolean
>  method virtual or_: boolean Lazy.t -> boolean
>  method virtual and_: boolean Lazy.t -> boolean
>  method virtual if_: 'a . 'a Lazy.t -> 'a Lazy.t -> 'a
> end
> 
> class false_class = object (self)
>  inherit boolean
>  method to_string = "false"
>  method not_ = new true_class
>  method or_ chk = Lazy.force chk
>  method and_ _ = (self :> boolean)
>  method if_ _ chk = Lazy.force chk
> end
> 
> and true_class = object (self)
>  inherit boolean
>  method to_string = "true"
>  method not_ = new false_class
>  method or_ _ = (self :> boolean)
>  method and_ chk = Lazy.force chk
>  method if_ chk _ = Lazy.force chk
> end
> 
> (* Default instances (that I would have preferred to inject
>   directly in definitions above) *)
> let false_ = new false_class
> and true_ = new true_class
> -8<-------------------------------------------------------------
> 
> Methods corresponding to message not_ in both false_class and true_class should ideally return a instance of, respectively, true_class and false_class that would have been created once and for all (instead of a fresh instance each time they are invoked). How does one achieve this in OCaml the functional way? (i.e., without resorting to references, but also, ideally, without resorting to lazy values)
> The use of immediate objects is IMO a better choice to implement booleans (because I don't want nor need to let users subclass false_class and true_class). Here is an attempt:
> 
> -8<-------------------------------------------------------------
> (* Using the same definition of boolean above *)
> let rec false_ = object (self)
>  inherit boolean
>  method to_string = "false"
>  method not_ = true_
>  method or_ chk = Lazy.force chk
>  method and_ _ = self
>  method if_ _ chk = Lazy.force chk
> end
> 
> and true_ = object (self)
>  inherit boolean
>  method to_string = "true"
>  method not_ = false_
>  method or_ _ = self
>  method and_ chk = Lazy.force chk
>  method if_ chk _ = Lazy.force chk
> end
> -8<-------------------------------------------------------------
> 
> I get “Error: This kind of expression is not allowed as right-hand side of `let rec'”. I wonder why OCaml does not accept the definition of recursive values like above (notice that references to false_ and true_ are “protected” by method definitions). Wouldn't it be safe to extend recursive definitions with this pattern?
> 
> Cheers,
> 
> Sébastien.
> 
> -- 
> Caml-list mailing list.  Subscription management and archives:
> https://sympa-roc.inria.fr/wws/info/caml-list
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
> 



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

* Re: [Caml-list] Questions about default instances
  2011-08-08  7:14 ` Jacques Garrigue
@ 2011-08-16  8:42   ` Sébastien Furic
  0 siblings, 0 replies; 3+ messages in thread
From: Sébastien Furic @ 2011-08-16  8:42 UTC (permalink / raw)
  To: caml-list

  Hello Jacques,

Le 08/08/2011 09:14, Jacques Garrigue a écrit :
> On 2011/08/05, at 1:22, Sébastien Furic wrote:
>
>> What is the usual way in OCaml to define mutually recursive classes that share default instances?
>
> There is no concept of "default instance" in OCaml, and as you discovered yourself,
> default instances create problems with initialization.
> Namely, an object constructor might attempt to access a default instance before it is built,
> so we need a way to know whether it is ready or not.

  But I still wonder why it is not possible to allow “direct let rec” in 
my example. Indeed, object creation is safe here since access to default 
instances is protected by method calls (there is a potential problem 
with initializers that may refer to, or call methods that refer to, 
not-yet-initialized objects but this can be statically checked, can't 
it?). As a benefit, it would avoid using lazy values in singleton 
patterns (which make the whole thing a bit unreadable IMO) and, in 
particular, the definition of not_ in my example would be more natural 
(I think about the poor readers of my code but also raw performance).

  Cheers,

  Sébastien.

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

end of thread, other threads:[~2011-08-16  8:43 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-08-04 16:22 [Caml-list] Questions about default instances Sébastien Furic
2011-08-08  7:14 ` Jacques Garrigue
2011-08-16  8:42   ` Sébastien Furic

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).