caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* strange behavior of the object type-checker
@ 1999-09-09 16:34 Pierre Boulet
  1999-09-09 19:43 ` Jerome Vouillon
  0 siblings, 1 reply; 11+ messages in thread
From: Pierre Boulet @ 1999-09-09 16:34 UTC (permalink / raw)
  To: caml-list

dear ocamllers,

I have finally decided to give a try to the object subsystem of ocaml
and I have stumbled on something very strange (for me, at least).

In the code I present below, after some parametric class creation, I
try to execute some method of an object. Three *successive* tries give
three *different* results: two different typing error messages and
finally success.

This code has been tested with ocaml 2.02 and the cvs version of
yesterday (8 sept.), ocaml 2.02+3. They give the same result.

My aim is to define a graph class which implements simple graph
algorithms such as depth first scanning. Anyway, here is the code.

class ['e] node g n0 =
  object (self)
    val mutable mark = true  (* a boolean to handle cycles *)
    method mark = mark
    method set_mark = mark <- true
    method unset_mark = mark <- false

    val mutable edges = ([] : 'e list)  (* outgoing edges *)
    method edges = edges
    method add_edge e = edges <- e::edges
    initializer g#add_node self  (* registration into the graph *)

    val n = n0              (* dummy printing *)
    method n = n
    method draw =
      Printf.printf "node: %i\n" n

    method iter fn fe =     (* depth first iteration *)
      if self#mark then
 	begin
	  fn self;
      	  self#unset_mark;
      	  List.iter (fun e -> e#iter fn fe) edges
      	end
  end;;

class ['n] edge g from_init towards_init =
  object (self)
    val mutable mark = true  (* a boolean to handle cycles *)
    method mark = mark
    method set_mark = mark <- true
    method unset_mark = mark <- false

    val from = (from_init : 'n)
    val towards = (towards_init : 'n)
    method from = from
    method towards = towards
    initializer g#add_edge self  (* registration into the graph *)
    initializer from#add_edge self  (* registration into the from node *)

    method draw =            (* dummy printing *)
      Printf.printf "edge: %i->%i\n" from#n towards#n

    method iter fn fe =      (* depth first iteration *)
      if self#mark then
 	begin
	  fe self;
      	  self#unset_mark;
	  towards#iter fn fe
  	end
  end;;

class ['n,'e] graph =
  object (self)
    val mutable nodes = ([] : 'n list)  (* list of nodes *)
    method nodes = nodes
    val mutable start_node = (None : 'n option)
    method start_node = start_node
    method set_start_node n = start_node <- Some n
    method add_node n =
      nodes <- n::nodes;
      match nodes with
	| [n] -> self#set_start_node n
	| _ -> ()

    val mutable edges = ([] : 'e list)  (* list of edges *)
    method edges = edges
    method add_edge e = edges <- e::edges

    method set_marks = 
      List.iter (fun n -> n#set_mark) nodes;
      List.iter (fun e -> e#set_mark) edges

    method iter (fn : 'n -> unit) (fe : 'e -> unit) = (* depth first iteration *)
      match start_node with
	| None -> failwith "set start node first"
	| Some n -> 
	    self#set_marks;
	    (n#iter fn fe : unit)
  end;;

let g = new graph;;
g#iter (fun n -> n#draw) (fun e -> e#draw);;
let n1 = new node g 1;;
g#iter (fun n -> n#draw) (fun e -> e#draw);;
(* until now, everything goes fine *)
let e11 = new edge g n1 n1;;
g#iter (fun n -> n#draw) (fun e -> e#draw);;
(* returns:
	Characters 17-18:
	This expression has type 'a edge node as 'a
	It has no method draw
*)	
g#iter (fun n -> n#draw) (fun e -> e#draw);;
(* returns:
	Characters 0-1:
	This expression has type
	  (('a edge as 'b) node as 'a, ('c node as 'd) edge as 'c) graph
	It has no method iter
*)	
g#iter (fun n -> n#draw) (fun e -> e#draw);;
(* returns as expected:
	node: 1
	edge: 1->1
	- : unit = ()
*)	

So what happens? It seems that adding an edge to the graph unsettles
the type checker...

any thought?

-- 
Pierre.




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

* Re: strange behavior of the object type-checker
  1999-09-09 16:34 strange behavior of the object type-checker Pierre Boulet
@ 1999-09-09 19:43 ` Jerome Vouillon
  1999-09-28 18:56   ` Can someone explain? skaller
  0 siblings, 1 reply; 11+ messages in thread
From: Jerome Vouillon @ 1999-09-09 19:43 UTC (permalink / raw)
  To: Pierre Boulet, caml-list

On Thu, Sep 09, 1999 at 06:34:19PM +0200, Pierre Boulet wrote:
> dear ocamllers,
> 
> I have finally decided to give a try to the object subsystem of ocaml
> and I have stumbled on something very strange (for me, at least).
> 
> In the code I present below, after some parametric class creation, I
> try to execute some method of an object. Three *successive* tries give
> three *different* results: two different typing error messages and
> finally success.

This is due to a type checker bug. Here is a patch (the bug should
also soon be corrected in cvs).

-- Jérôme

Index: typing/ctype.ml
===================================================================
RCS file: /net/pauillac/caml/repository/csl/typing/ctype.ml,v
retrieving revision 1.65
diff -u -u -r1.65 ctype.ml
--- ctype.ml    1999/02/24 15:21:49     1.65
+++ ctype.ml    1999/09/09 19:35:33
@@ -1068,7 +1068,8 @@
   let t1' = expand_head env t1 in
   let t2' = expand_head env t2 in
   (* Expansion may have changed the representative of the types... *)
-  let t1' = repr t1' and t2' = repr t2' in
+  let t1' = expand_head env t1' in
+  let t2' = expand_head env t2' in
   if t1' == t2' then () else
 
   let t1 = repr t1 and t2 = repr t2 in




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

* Can someone explain?
  1999-09-09 19:43 ` Jerome Vouillon
@ 1999-09-28 18:56   ` skaller
  1999-10-04  8:23     ` Pierre Weis
  0 siblings, 1 reply; 11+ messages in thread
From: skaller @ 1999-09-28 18:56 UTC (permalink / raw)
  To: caml-list

Is there a way to access a value of a class instance?
If so, what is the syntax? If not, what is the purpose
of allowing 'val' bindings in class declarations in module
signatures?

Similarly, what is the purpose of allowing 'virtual'
methods in class types and class declarations in
module signatures?

I have a doubly linked list class, and concatenating 
two lists takes 100 times longer in ocaml using my
code than pythons list concatenation function,
which is written in C.

My code isn't optimal (given the particular data structure
I've chosen) because the concat function cannot
get at values of the class. Excerpt given below.
Any advice appreciated. (A high speed mutable list
in the standard library would be even better :-)

module DoublyLinkedList : BiDirectional =
  struct (* doubly linked list type *)
    type 't d_node = 
      {
        mutable nxt: 't iterator; 
        mutable prv: 't iterator;
        mutable data: 't
      }

    and 't iterator = 
      Empty 
      | Node of 't d_node

    let next x = match x with Empty -> Empty | Node n -> n.nxt
    let deref x = match x with Empty -> None | Node n -> Some n.data

    class ['t] dlist = 
      object(self)
        val mutable first': 't iterator = Empty
        val mutable last':  't iterator = Empty
        val mutable size: int = 0

        method private init node = 
          last' <- node; 
          first' <- node;
          size <- 1

        method length = size

        (* STL style mutators *)
        method push_back (data':'t): unit = 
          match last' with
          | Empty -> self#init (Node {nxt=Empty; prv=Empty; data=data'})
          | Node fin -> 
            let tmp = Node {nxt=Empty; prv=last'; data=data'}  in 
              fin.nxt <- tmp;
              last' <- tmp;
              size <- size + 1

        method first = first'
     end

    let concat (x:'a dlist) (y:'a dlist) :'a dlist = 
      let z = new dlist in
      let i = ref x#first in
      while deref !i <> None do 
        match deref !i with 
        | Some v -> z#push_back v; i := next !i 
        | None -> assert false
      done;
      let j = ref y#first in
      while deref !j <> None do 
        match deref !j with
        | Some v -> z#push_back v; j := next !j 
        | None -> assert false
      done;
      z
end

-- 
John Skaller, mailto:skaller@maxtal.com.au
1/10 Toxteth Rd Glebe NSW 2037 Australia
homepage: http://www.maxtal.com.au/~skaller
downloads: http://www.triode.net.au/~skaller




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

* Re: Can someone explain?
  1999-09-28 18:56   ` Can someone explain? skaller
@ 1999-10-04  8:23     ` Pierre Weis
  1999-10-04 22:57       ` skaller
  0 siblings, 1 reply; 11+ messages in thread
From: Pierre Weis @ 1999-10-04  8:23 UTC (permalink / raw)
  To: skaller; +Cc: caml-list

> Is there a way to access a value of a class instance?
> If so, what is the syntax? If not, what is the purpose
> of allowing 'val' bindings in class declarations in module
> signatures?

The only way to access a value of a class instance is via method
invocation: you have to define a method that returns the value.

> Similarly, what is the purpose of allowing 'virtual'
> methods in class types and class declarations in
> module signatures?

Virtual methods are methods that are declared but not implemented:
sub-classes must define them.

> I have a doubly linked list class, and concatenating 
> two lists takes 100 times longer in ocaml using my
> code than pythons list concatenation function,
> which is written in C.

Wao! I would like to be able to run your benchmark, since I will be
glad to try to use the Python's way of handling lists to speed up the
Caml list package by a factor of 100! We worked hard on this point,
and Caml lists (and more generally similar data structure
manipulations) are supposed to be among the fastest known
implementations available. So, could you please send a complete
reproducible benchmark ?

> My code isn't optimal (given the particular data structure
> I've chosen) because the concat function cannot
> get at values of the class. Excerpt given below.
> Any advice appreciated. (A high speed mutable list
> in the standard library would be even better :-)

As you suggested, we may add extra list manipulation modules in the
standard library, such as mutable lists or doubly linked
lists. Mutable lists are easy to implement and there is no doubt that
many Caml programmers have already written such kind of packages and
can contribute.

About your own program, I cannot comment on efficiency, since I do not
catch the general idea of the design: I am a bit surprised by the
melting of object oriented features and regular algebraic data type
manipulation to implement lists. Could you please explain a bit the
logic of your code (in particular the only comment
(* STL style mutators *) is a kind of puzzle for me) ?

Best regards,

Pierre Weis

INRIA, Projet Cristal, Pierre.Weis@inria.fr, http://cristal.inria.fr/~weis/

> module DoublyLinkedList : BiDirectional =
>   struct (* doubly linked list type *)
>     type 't d_node = 
>       {
>         mutable nxt: 't iterator; 
>         mutable prv: 't iterator;
>         mutable data: 't
>       }
> 
>     and 't iterator = 
>       Empty 
>       | Node of 't d_node
> 
>     let next x = match x with Empty -> Empty | Node n -> n.nxt
>     let deref x = match x with Empty -> None | Node n -> Some n.data
> 
>     class ['t] dlist = 
>       object(self)
>         val mutable first': 't iterator = Empty
>         val mutable last':  't iterator = Empty
>         val mutable size: int = 0
> 
>         method private init node = 
>           last' <- node; 
>           first' <- node;
>           size <- 1
> 
>         method length = size
> 
>         (* STL style mutators *)
>         method push_back (data':'t): unit = 
>           match last' with
>           | Empty -> self#init (Node {nxt=Empty; prv=Empty; data=data'})
>           | Node fin -> 
>             let tmp = Node {nxt=Empty; prv=last'; data=data'}  in 
>               fin.nxt <- tmp;
>               last' <- tmp;
>               size <- size + 1
> 
>         method first = first'
>      end
> 
>     let concat (x:'a dlist) (y:'a dlist) :'a dlist = 
>       let z = new dlist in
>       let i = ref x#first in
>       while deref !i <> None do 
>         match deref !i with 
>         | Some v -> z#push_back v; i := next !i 
>         | None -> assert false
>       done;
>       let j = ref y#first in
>       while deref !j <> None do 
>         match deref !j with
>         | Some v -> z#push_back v; j := next !j 
>         | None -> assert false
>       done;
>       z
> end
> 
> -- 
> John Skaller, mailto:skaller@maxtal.com.au
> 1/10 Toxteth Rd Glebe NSW 2037 Australia
> homepage: http://www.maxtal.com.au/~skaller
> downloads: http://www.triode.net.au/~skaller





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

* Re: Can someone explain?
  1999-10-04  8:23     ` Pierre Weis
@ 1999-10-04 22:57       ` skaller
  1999-10-05  9:43         ` Jerome Vouillon
                           ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: skaller @ 1999-10-04 22:57 UTC (permalink / raw)
  To: Pierre Weis; +Cc: caml-list

Pierre Weis wrote:
> 
> > Is there a way to access a value of a class instance?
> > If so, what is the syntax? If not, what is the purpose
> > of allowing 'val' bindings in class declarations in module
> > signatures?
> 
> The only way to access a value of a class instance is via method
> invocation: you have to define a method that returns the value.

	Thanks, but you have not answered the real question here:
WHY are the values present in the interface when they are not accessible
via the interface?
 
> > Similarly, what is the purpose of allowing 'virtual'
> > methods in class types and class declarations in
> > module signatures?
> 
> Virtual methods are methods that are declared but not implemented:
> sub-classes must define them.

	Again, I knew that, the real question is WHY this information
is in the _interface_??

> > I have a doubly linked list class, and concatenating
> > two lists takes 100 times longer in ocaml using my
> > code than pythons list concatenation function,
> > which is written in C.
> 
> Wao! I would like to be able to run your benchmark, since I will be
> glad to try to use the Python's way of handling lists to speed up the
> Caml list package by a factor of 100! 

	Sigh. I have just examined the Python implementation and 
the reason is that Python uses arrays for lists. This makes
insertion, deletion, and concatenation O(n), but it uses
fast machine primatives (memcpy) and does only one allocation.
My doubly linked list is the same order for appending,
but it laboriously copies each node one by one, and has to do an
allocation
for each one, and has to search for the i'th location as well.
So it is much slower than the Python equivalent.

	I will change the representation to match Python, using an
array (or perhaps a doubly linked list of arrays), and report back.

> We worked hard on this point,
> and Caml lists (and more generally similar data structure
> manipulations) are supposed to be among the fastest known
> implementations available. So, could you please send a complete
> reproducible benchmark ?

	You would need the WHOLE interpreter :-)
I will make that available in the near future, asking for help
to speed up the implementation.

	I think it would be VERY useful to have an ocaml written
Python interpreter/compiler as fast as, or faster than, CPython.
There are a lot of Python users out there who could be introduced
to ocaml this way, and gain immediate benefits from a faster
implementation (particularly the compiler).

> As you suggested, we may add extra list manipulation modules in the
> standard library, such as mutable lists or doubly linked
> lists. Mutable lists are easy to implement and there is no doubt that
> many Caml programmers have already written such kind of packages and
> can contribute.

	Excellent! This would be an ideal situation.

	The other thing I would beg for is to fix the parser
and lexer to be re-entrant [purely functional]. At present, state
information
must be kept in global store :-(

	It would also be useful if the parser could support nested
parser calls as the lexer can: this is possible with the lexer,
because the lexbuf is always in a state to read the next lexeme,
however it is not possible with the parser because it may have read
a token ahead of the production being reduced.

> About your own program, I cannot comment on efficiency, since I do not
> catch the general idea of the design: I am a bit surprised by the
> melting of object oriented features and regular algebraic data type
> manipulation to implement lists. Could you please explain a bit the
> logic of your code (in particular the only comment
> (* STL style mutators *) is a kind of puzzle for me) ?

Sorry for brevity. STL is the C++ Standard Template Library,
it contains efficient codes for manipulating data structures
with generic algorithms, using iterators (i.e. pointer like things).

> > module DoublyLinkedList : BiDirectional =
> >   struct (* doubly linked list type *)
> >     type 't d_node =
> >       {
> >         mutable nxt: 't iterator;
> >         mutable prv: 't iterator;
> >         mutable data: 't
> >       }

A doubly linked list has a node containing two pointers,

	nxt -- a pointer to the next node object
	prv -- a pointer to the previous node object
	data -- the data

	
> >     and 't iterator =
> >       Empty
> >       | Node of 't d_node

	This is the type of a pointer to a node. In ocaml,
references are used for pointers, so the type of a pointer is
just that of a node, except that the pointer can advance
'off the end of the list' and so can refer to no node,
and thus the two cases 'Empty' for off the end, and Node
for pointing to an actual node.

> >     let next x = match x with Empty -> Empty | Node n -> n.nxt
> >     let deref x = match x with Empty -> None | Node n -> Some n.data

	next gets a pointer to the next node, given a pointer to some
node x. deref gets the data, given an iterator. in C++:

	x = ++x // next
	*x      // deref

is the notation used, however the implementation is

	x->nxt
	x->data

> >     class ['t] dlist =
> >       object(self)
> >         val mutable first': 't iterator = Empty

A class is used here to represent the whole list. It is an object
simply because that is the intended use: as a mutable object
with various mutators. In retrospect, this is probably a mistake.

-- 
John Skaller, mailto:skaller@maxtal.com.au
1/10 Toxteth Rd Glebe NSW 2037 Australia
homepage: http://www.maxtal.com.au/~skaller
downloads: http://www.triode.net.au/~skaller




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

* Re: Can someone explain?
  1999-10-04 22:57       ` skaller
@ 1999-10-05  9:43         ` Jerome Vouillon
  1999-10-05 19:35         ` Gerd Stolpmann
  1999-10-05 21:42         ` Can someone explain? Lyn A Headley
  2 siblings, 0 replies; 11+ messages in thread
From: Jerome Vouillon @ 1999-10-05  9:43 UTC (permalink / raw)
  To: skaller

On Tue, Oct 05, 1999 at 08:57:20AM +1000, skaller wrote:
> Pierre Weis wrote:
> > 
> > > Is there a way to access a value of a class instance?
> > > If so, what is the syntax? If not, what is the purpose
> > > of allowing 'val' bindings in class declarations in module
> > > signatures?
> > 
> > The only way to access a value of a class instance is via method
> > invocation: you have to define a method that returns the value.
> 
> 	Thanks, but you have not answered the real question here:
> WHY are the values present in the interface when they are not accessible
> via the interface?
>  
> > > Similarly, what is the purpose of allowing 'virtual'
> > > methods in class types and class declarations in
> > > module signatures?
> > 
> > Virtual methods are methods that are declared but not implemented:
> > sub-classes must define them.
> 
> 	Again, I knew that, the real question is WHY this information
> is in the _interface_??

This information is in the interface because a class declaration can
also be used for inheritance, and a subclass can access the instance
variables of its parent and provide an implementation for the
virtual methods.

-- Jérôme




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

* Re: Can someone explain?
  1999-10-04 22:57       ` skaller
  1999-10-05  9:43         ` Jerome Vouillon
@ 1999-10-05 19:35         ` Gerd Stolpmann
  1999-10-06  9:42           ` skaller
  1999-10-08  0:17           ` Problem of coercion in recursive class definitions Peter Schrammel
  1999-10-05 21:42         ` Can someone explain? Lyn A Headley
  2 siblings, 2 replies; 11+ messages in thread
From: Gerd Stolpmann @ 1999-10-05 19:35 UTC (permalink / raw)
  To: skaller; +Cc: caml-list

On Tue, 05 Oct 1999, John Skaller wrote:

>	The other thing I would beg for is to fix the parser
>and lexer to be re-entrant [purely functional]. At present, state
>information
>must be kept in global store :-(
>
>	It would also be useful if the parser could support nested
>parser calls as the lexer can: this is possible with the lexer,
>because the lexbuf is always in a state to read the next lexeme,
>however it is not possible with the parser because it may have read
>a token ahead of the production being reduced.

I have recently posted a solution for this problem by turning the
generated parser into a class; side-effects still exist but are
limited to the object representing the parser. You can simply create
several instances of the same parser, and nested parser calls are possible
if the sub-parser resides in a different object than the calling parser.

The solution requires a complicated Makefile, and I again suggest to
support an object-oriented parser generator in one of the next caml
releases. You find a description of the idea here:
	http://pauillac.inria.fr/caml/caml-list/1575.html

A working application is my XML parser, look at the files markup_yacc.mly and
Makefile.code in
	http://people.darmstadt.netsurf.de/Gerd.Stolpmann/ocaml/markup-0.2.tar.gz
As any real application, this parser is much more complicated than necessary to
demonstrate the principle...

Gerd

--
----------------------------------------------------------------------------
Gerd Stolpmann      Telefon: +49 6151 997705 (privat)
Viktoriastr. 100             
64293 Darmstadt     EMail:   Gerd.Stolpmann@darmstadt.netsurf.de (privat)
Germany                     
----------------------------------------------------------------------------




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

* Re: Can someone explain?
  1999-10-04 22:57       ` skaller
  1999-10-05  9:43         ` Jerome Vouillon
  1999-10-05 19:35         ` Gerd Stolpmann
@ 1999-10-05 21:42         ` Lyn A Headley
  1999-10-06 10:17           ` skaller
  2 siblings, 1 reply; 11+ messages in thread
From: Lyn A Headley @ 1999-10-05 21:42 UTC (permalink / raw)
  To: skaller; +Cc: caml-list

>>>>> "skaller" == skaller  <skaller@maxtal.com.au> writes:

    >> The only way to access a value of a class instance is via
    >> method invocation: you have to define a method that returns the
    >> value.

    skaller> 	Thanks, but you have not answered the real question
    skaller> here: WHY are the values present in the interface when
    skaller> they are not accessible via the interface?

good question.
 
    >> > Similarly, what is the purpose of allowing 'virtual' >
    >> methods in class types and class declarations in > module
    >> signatures?
    >> 
    >> Virtual methods are methods that are declared but not
    >> implemented: sub-classes must define them.

    skaller> 	Again, I knew that, the real question is WHY this
    skaller> information is in the _interface_??


[I know]


    skaller> 	I will change the representation to match Python,
    skaller> using an array (or perhaps a doubly linked list of
    skaller> arrays), and report back.

Here's an idea: just hijack the Python implementation and provide an
ocaml interface.  This has the advantages that (1) it has been
hand-tuned for efficiency for years and (2) that it will export a
similar interface to the ocaml code as has existed for the C code for
years, thus allowing a smoother transition to ocaml for python
extension writers.

Come to think of it, why not do that for /all/ the builtin types?
These will also be useful for use by code generated by viperc.


    skaller> 	You would need the WHOLE interpreter :-) I will make
    skaller> that available in the near future, asking for help to
    skaller> speed up the implementation.

yum. <smacks his chops noisily>

    skaller> 	I think it would be VERY useful to have an ocaml
    skaller> written Python interpreter/compiler as fast as, or faster
    skaller> than, CPython.  There are a lot of Python users out there
    skaller> who could be introduced to ocaml this way, and gain
    skaller> immediate benefits from a faster implementation
    skaller> (particularly the compiler).

Me too!  This could be a big thing for ocaml.  Darn it, now you've got
me all psyched about ocaml again.  Too bad I already committed to
Eiffel for my latest project.

[snip]

    skaller> A doubly linked list has a node containing two pointers,

[snip]
    >> > and 't iterator = > Empty > | Node of 't d_node

    skaller> 	This is the type of a pointer to a node. In ocaml,

An ocaml "port" of STL would kick ass.  especially, think how well
iterators would combine with closures!  The C++ notion of "function
objects" and "adaptors" looks clumsy in comparison (e.g. you cannot
create a localized class object)


    skaller> A class is used here to represent the whole list. It is
    skaller> an object simply because that is the intended use: as a
    skaller> mutable object with various mutators. In retrospect, this
    skaller> is probably a mistake.

why?


-Lyn




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

* Re: Can someone explain?
  1999-10-05 19:35         ` Gerd Stolpmann
@ 1999-10-06  9:42           ` skaller
  1999-10-08  0:17           ` Problem of coercion in recursive class definitions Peter Schrammel
  1 sibling, 0 replies; 11+ messages in thread
From: skaller @ 1999-10-06  9:42 UTC (permalink / raw)
  To: Gerd.Stolpmann; +Cc: caml-list

Gerd Stolpmann wrote:
> 
> On Tue, 05 Oct 1999, John Skaller wrote:
> >       The other thing I would beg for is to fix the parser
> >and lexer to be re-entrant [purely functional]. At present, state
> >information
> >must be kept in global store :-(
> >

> I have recently posted a solution for this problem

> The solution requires a complicated Makefile

I have seen your solution, but I'd prefer not to rely on
this kind of thing in code which may be distributed: it's fragile,
depending on implementation dependent knowledge about the form
of the files ocamlyacc generates.

However, it seems like a good proof of principle, and I'm 
sure we're agreeing a _standard_ solution in the next
version of ocaml would be ideal.

-- 
John Skaller, mailto:skaller@maxtal.com.au
1/10 Toxteth Rd Glebe NSW 2037 Australia
homepage: http://www.maxtal.com.au/~skaller
downloads: http://www.triode.net.au/~skaller




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

* Re: Can someone explain?
  1999-10-05 21:42         ` Can someone explain? Lyn A Headley
@ 1999-10-06 10:17           ` skaller
  0 siblings, 0 replies; 11+ messages in thread
From: skaller @ 1999-10-06 10:17 UTC (permalink / raw)
  To: Lyn A Headley; +Cc: caml-list

Lyn A Headley wrote:

> Here's an idea: just hijack the Python implementation and provide an
> ocaml interface.  This has the advantages that (1) it has been
> hand-tuned for efficiency for years and (2) that it will export a
> similar interface to the ocaml code as has existed for the C code for
> years, thus allowing a smoother transition to ocaml for python
> extension writers.

	I thought of that, and I was tempted!	
However, I decided against it for several reasons.

	First, while it leverages the Python code, it creates 
a headache for memory management, since Python does it's own,
which doesn't sit well with ocaml garbage collector.

	Second, it means any extensions I wish to write
will have to be written in C. This defeats the advantages
of using ocaml. For example, I have already added a Rational
type using the num module: I did it in only a few hours,
and added long integers at the same time: the num long integers
are already faster than the python ones.

	Third, it becomes sensitive to the C implementation
of Python which changes with versions, and depends on 
the whims of developers who may have different goals than I do.

> Come to think of it, why not do that for /all/ the builtin types?
> These will also be useful for use by code generated by viperc.

	So will the ocaml based run time, if the compiler back end
generates ocaml :-)

	There are two versions of Python: CPython (C) and JPython (Java).
Both suffer from portability problems, due to the base language,
compared with ocaml (IMHO). Both suffer from inferior technology,
compared with ocaml (IMHO).

	My plan is to suport _pure_ python, not C based extensions:
just the same as JPython cannot use C based extensions. 
I suspect that the C -> ocaml -> python wrapper chain for 
extensions representing external interfaces (operating system
services and access to C libraries) is probably easier
than the C -> CPython extension -> Python.

	However, a lot of C modules in python are only
for efficiency, and it is better to rewrite these in Python
and use the compiler, or, in ocaml if really necessary.

> Darn it, now you've got
> me all psyched about ocaml again.  Too bad I already committed to
> Eiffel for my latest project.

	Oh well :-)

> An ocaml "port" of STL would kick ass.  

	The code I showed is part of a set of modules I'm developing
that (attempt to) provide ocaml representations of the same ideas.
However, this has a lower priority than the python compiler.

>especially, think how well
> iterators would combine with closures!  The C++ notion of "function
> objects" and "adaptors" looks clumsy in comparison (e.g. you cannot
> create a localized class object)

	Yes. In fact, I have a contract to write a book on STL,
but I gave up precisely because of this. I tried to tell the
committee, but they just didn't understand. STL isn't that useful in
C++, because the core language fails to support
nested functions, when it could. Furthermore,
the committee broke the way unions work, so that they
cannot be used to implement variants easily.

	I prefer to work with a language whose developers
actually know some mathematics.

>     skaller> A class is used here to represent the whole list. It is
>     skaller> an object simply because that is the intended use: as a
>     skaller> mutable object with various mutators. In retrospect, this
>     skaller> is probably a mistake.
> 
> why?

	Because, the functional operations, such as concatenation,
require access to the private data, and cannot get it. There are
no friends in ocaml: the correct solution is to abandon the class
and use an algebraic data structure, and restrict the interface 
of the module so it becomes abstract.

	There is another reason: class methods cannot be polymorphic,
wheres plain functions of modules can be (independently of instantiation
of type parameters of the module).

-- 
John Skaller, mailto:skaller@maxtal.com.au
1/10 Toxteth Rd Glebe NSW 2037 Australia
homepage: http://www.maxtal.com.au/~skaller
downloads: http://www.triode.net.au/~skaller




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

* Problem of coercion in recursive class definitions
  1999-10-05 19:35         ` Gerd Stolpmann
  1999-10-06  9:42           ` skaller
@ 1999-10-08  0:17           ` Peter Schrammel
  1 sibling, 0 replies; 11+ messages in thread
From: Peter Schrammel @ 1999-10-08  0:17 UTC (permalink / raw)
  Cc: caml-list

Sorry if this might be a RTFM problem but I tried to solve it for the
last week but didn't make it.
I attached two modules I wrote, The xmlParser.ml module is an
instantiation of the xmlState.ml*. But it gives me the error:

The method set_xml_cache has type cache:my_cache -> my_state0
but is expected to have type
  cache:(my_content_handler #XmlState.xml_state as 'b,
my_content_handler)
        XmlState.xml_cache ->
  (< debug : unit -> unit;
     get_ch_factory :
       (my_content_handler Grove.small_obj_factory, Unicode.ustring)
       Grove.factory;
     get_xml_cache : ('b, my_content_handler) XmlState.xml_cache;
     get_xml_zipper : ('b, my_content_handler) XmlState.xml_zipper;
     set_ch_factory :
       factory:(my_content_handler Grove.small_obj_factory,
Unicode.ustring)
               Grove.factory ->
       'b;
     set_xml_cache : 'a;
     set_xml_zipper :
       zipper:('b, my_content_handler) XmlState.xml_zipper -> 'c;
     .. > as 'c) as 'a
Type my_state0 = < .. > is not compatible with type 'c
Self type cannot escape its class

Although I tried the coercion trick from page 40 of the manual.
Thanks for any help,
Peter
--
Peter Schrammel
UniBw-Muenchen 106/2/116
85579 Neubiberg
ICQ: 5469131

 name="xmlState.mli"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="xmlState.mli"

open Unicode;;
type 'a content = START of 'a | TAG of 'a * 'a | EMPTY of 'a | PCDATA of 'a
type cachetype = PERef of Unicode.ustring | ERef of  Unicode.ustring 

type context = Unicode.ustring Grove.big_pattern
type rOD_content = XmlTypes.refOrData content

(*state and contenthandler are parameter*)

class virtual ['state] content_handler :
  object
    constraint 'state = 'chandler #xml_state
      (*parse: get's some relevant information out of the content.
         for example the name of the element or the name of an id*)
    method virtual parse : state:'state -> 'state
      (*cache: bring this information into the cache *)
    method virtual cache : state:'state -> 'state
      (*uncache: remove it from there*)
    method virtual uncache : state:'state -> 'state
    method virtual deref : state:'state -> dtd:bool -> peref:bool ->
      eref:bool -> chref:bool -> XmlTypes.refOrData list * 'state
  end

and virtual ['chandler] xml_state :
  object ('self)
    constraint 'chandler = 'state #content_handler
    method virtual get_xml_zipper : ('state,'chandler) xml_zipper
    method virtual set_xml_zipper : zipper:('state,'chandler) xml_zipper -> 'self

    method virtual get_xml_cache  : ('state,'chandler) xml_cache
    method virtual set_xml_cache  : cache:('state,'chandler) xml_cache -> 'self

    method virtual get_ch_factory : ('chandler Grove.small_obj_factory,ustring) Grove.factory
    method virtual set_ch_factory : factory:('chandler Grove.small_obj_factory,ustring) Grove.factory
      -> 'state

end

and virtual ['state,'chandler] 
      xml_node : content:rOD_content -> chandler:'chandler ->
object
  constraint 'state = 'chandler #xml_state
  constraint 'chandler = 'state #content_handler

  inherit Zipper.zip_obj
  inherit ['state] content_handler

  method virtual get_content : rOD_content
  method virtual set_content : content:rOD_content -> unit
  method virtual set_chandler : chandler:'chandler -> unit
end

and  virtual ['state,'chandler] xml_zipper : root:('state,'chandler) xml_node ->
  object
    constraint 'state = 'chandler #xml_state
    constraint 'chandler = 'state #content_handler

    inherit [('state,'chandler) xml_node] Zipper.zipper
    method virtual build : preparsed:PreParser.tagOrPCData list ->
        state:'state -> 'state

end

and virtual ['state,'chandler] xml_cache :
object ('cache)
  constraint 'state = 'chandler #xml_state
  constraint 'chandler = 'state #content_handler

  method virtual set : key:cachetype -> data:('state,'chandler) xml_node -> 'cache
  method virtual get : key:cachetype -> ('state,'chandler) xml_node
  method virtual remove : key:cachetype -> 'cache
end


 name="xmlParser.ml"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="xmlParser.ml"

open Unicode;;
let us = Unicode.ustring_of_string;;
let uc = Unicode.uchar_of_char;;
let su = string_of_ustring;;
open XmlState;;

exception Parser_Error;;
exception Cache_Error;;

(*
let debug_cachetype = function
    PERef u -> print_string (su u)
  | ERef u -> print_string (su u)
*)


class virtual my_state0 option:(opt : Eoption.eoption) xzipper:(z : my_zipper) 
    ch_factory:(cf : (my_content_handler Grove.small_obj_factory,ustring) Grove.factory) = 
(*I have to declare this class to be able to coerce my_state*)
  object
    inherit [my_content_handler] xml_state
    inherit Debug.debug

    method virtual get_xml_cache  : my_cache
    method virtual set_xml_cache  : cache:my_cache -> my_state0

    method virtual get_xml_zipper : my_zipper
    method virtual set_xml_zipper : zipper:my_zipper -> my_state0


    method virtual get_ch_factory : (my_content_handler Grove.small_obj_factory,ustring) Grove.factory
    method virtual set_ch_factory : factory:(my_content_handler Grove.small_obj_factory,ustring) 
        Grove.factory -> my_state0

    method virtual get_entity_proxy : Catalog.entity_proxy
    method virtual get_option : Eoption.eoption

end

and my_content_handler =
  object
    inherit [my_state] content_handler
    method parse  state:(s : my_state) = s
    method cache  state:(s : my_state) = s
    method uncache  state:(s : my_state) = s
    method deref  state:(s : my_state) dtd:(dtd : bool) peref:(per : bool) 
         eref:(er : bool) chref:(chr : bool) = (([] : XmlTypes.refOrData list),(s : my_state))
    method debug () = ()
  end


and my_node content:(cnt : rOD_content) chandler:(ch : my_content_handler) = 
  object
    inherit [my_state,my_content_handler] xml_node content:cnt chandler:ch
    val mutable content = cnt
    val mutable chandler = ch
    method get_content = content
    method set_content content:c = content <-cnt
    method set_chandler chandler:ch = chandler<-ch
    method parse = chandler#parse
    method cache = chandler#cache
    method uncache = chandler#uncache
    method deref = chandler#deref
    method debug () = ()
  end

and my_zipper root:(root : my_node) =
  object
    inherit [my_state,my_content_handler] xml_zipper root:root
    method build preparsed:(pre : PreParser.tagOrPCData list) state:(state : my_state) =
      state
end

and  my_cache = 
  object (self)
    inherit  [my_state,my_content_handler] xml_cache

    val mutable cache = Hashtbl.create size:191

    method set key:(k : cachetype)  data:(d :  my_node) = 
      try let _ = Hashtbl.find cache key:k
          in
          raise Cache_Error
      with Not_found -> Hashtbl.add cache key:k data:d;self

    method remove key:k = Hashtbl.remove cache key:k;self

    method get key:k = Hashtbl.find cache key:k

(*    method debug () = 
      let print k (d :  my_node)= 
        debug_cachetype k;
        print_string " -> ";
        d#debug ();
        print_newline ();
      in
      Hashtbl.iter fun:(fun key:k data:d -> print k d) cache;*)
  end

and my_state option:(opt :Eoption.eoption)  xzipper:(xzipper : my_zipper) 
    ch_factory:( chfac : (my_content_handler Grove.small_obj_factory,ustring) Grove.factory) =
  object(self)
    inherit my_state0

    val mutable xc = new my_cache
    val mutable ep = new Catalog.entity_proxy option:opt

    val mutable xz = xzipper
    val mutable op = opt
    val mutable chf = chfac
        
    method get_xml_zipper = xz
    method set_xml_zipper zipper:z = xz <- z;(self : #my_state0 :> my_state0)
        
    method get_xml_cache = xc
    method set_xml_cache cache:c = xc <- c;(self : #my_state0 :> my_state0)
        
    method get_ch_factory = chfac
    method set_ch_factory factory:c = chf <- c;(self : #my_state0 :> my_state0)
      
    method get_entity_proxy = ep
        
    method get_option = op
        
    method debug () = ()
  end





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

end of thread, other threads:[~1999-10-08  9:59 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1999-09-09 16:34 strange behavior of the object type-checker Pierre Boulet
1999-09-09 19:43 ` Jerome Vouillon
1999-09-28 18:56   ` Can someone explain? skaller
1999-10-04  8:23     ` Pierre Weis
1999-10-04 22:57       ` skaller
1999-10-05  9:43         ` Jerome Vouillon
1999-10-05 19:35         ` Gerd Stolpmann
1999-10-06  9:42           ` skaller
1999-10-08  0:17           ` Problem of coercion in recursive class definitions Peter Schrammel
1999-10-05 21:42         ` Can someone explain? Lyn A Headley
1999-10-06 10:17           ` skaller

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).