caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] A G'Caml question
@ 2001-06-20  3:16 Brian Rogoff
  2001-06-25 17:11 ` "Re: [Caml-list] A G'Caml question" + additional info Jun Furuse
  0 siblings, 1 reply; 4+ messages in thread
From: Brian Rogoff @ 2001-06-20  3:16 UTC (permalink / raw)
  To: caml-list

Hi,
    One of the important issues with overloading is whether one has 
to define a "generic" function all in one place or if you can build it up 
piecemeal from already existing generic functions. I played a bit, learned 
some things, and now I have some questions. 

I start with the obvious "plus" function, and extend it so that in 
concatenates strings. 

# generic plus = case
    int -> int -> int => (+)
  | float -> float -> float => (+.) ;;  
val plus : $a -> $a -> $a [ int -> int -> int 
                          | float -> float -> float  ] =
  <fun>
# plus 1 2;;
- : int = 3
# plus 1.0 2.0;;
- : float = 3
# plus "1" "2";;
This generic full instance is used with type string -> string -> string
 which is not its valid instance of 
$a -> $a -> $a [ int -> int -> int 
               | float -> float -> float  ]
# generic plus = case 
  string -> string -> string => (^) 
  | $a -> $a -> $a => plus ;;  
val plus : $a -> $a -> $a
  [ string -> string -> string 
  | $a -> $a -> $a && plus : $a -> $a -> $a  ] = <fun>
# plus 1 1;;
- : int = 2
# plus "1" "2.0";;
- : string = "12.0"

Cool, for some very simple cases, it we can incrementally build up a 
generic function from pieces. I think it's obvious that we'll get into 
trouble with recursive generics. 

# generic rec print = case 
  | int -> unit => print_int
  | string -> unit => print_string
  | $a list -> unit => 
      function [] -> ()
      |   x :: xs -> print x; print xs
  ;;            
  val print : $a -> unit
  [ int -> unit 
  | string -> unit 
  | $a list -> unit && print : $a -> unit && print : $a list -> unit  ] =
  <fun>
# print 23;;
23- : unit = ()
# print 23.0;;
This generic full instance is used with type float -> unit
 which is not its valid instance of 
$a -> unit
[ int -> unit 
| string -> unit 
| $a list -> unit && print : $a -> unit && print : $a list -> unit  ]

OK, lets try the same thing as before, 

# generic rec print = case float -> unit => print_float | $a -> unit =>
print;;
val print : $a -> unit
  [ float -> unit 
  | $a -> unit && print : $a -> unit  ] = <fun>

# print 1.0;;
1- : unit = ()
# print 1 (* infinite loop! *)

Well, that should be expected, since we try and print an int, which isn't
a float, so we try (from $a -> unit => print) to print, ad infinitum. Is
there a way out? If we try and rename print and call that, it still won't work, 
since we want a form of "open recursion" here: if I add a case for float I 
want to be able to print float lists. With the original print
reinstalled...

# let print' = print ;;
val print' : $a -> unit [ $a -> unit && print : $a -> unit  ] = <fun>
# generic rec print = case float -> unit => print_float | $a -> unit =>
print';;
val print : $a -> unit
  [ float -> unit 
  | $a -> unit && print' : $a -> unit  ] = <fun>
# print 1.0;;
1- : unit = ()
# print [1;2;3];;
123- : unit = ()
# print [1.0;2.0;3.0];; 
This generic full instance is used with type float list -> unit
 which is not its valid instance of 
$a -> unit [ float -> unit 
           | $a -> unit && print' : $a -> unit  ]

Is there some trick to build up recursive generics by parts? One thing 
to do is to break the recursive generic into a non-recursive generic
and a recursive function which applies the generic to the elements, 
but that feels like cheating. Maybe open recursion for generics would 
be desirable, so that I can add a new case and have the recursive call in 
an existing case call the new one? Yes, it's a half baked idea, but maybe 
a better cook can finish baking...

-- Brian



-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

* "Re: [Caml-list] A G'Caml question" + additional info
  2001-06-20  3:16 [Caml-list] A G'Caml question Brian Rogoff
@ 2001-06-25 17:11 ` Jun Furuse
  2001-06-28  2:21   ` Patrick M Doane
  0 siblings, 1 reply; 4+ messages in thread
From: Jun Furuse @ 2001-06-25 17:11 UTC (permalink / raw)
  To: caml-list

Hello,

Thanks your comment about the exntesion. It is natural idea to extend
generic values and it must be definitively required. This extension
of generic values cannot be handled by the normal redefinition which
is available in G'Caml. They essentially requires its own syntax and
typing, which are still not be implemented...

Let me explain details using your example:

> # generic rec print = case 
>   | int -> unit => print_int
>   | string -> unit => print_string
>   | $a list -> unit => 
>       function [] -> ()
>       |   x :: xs -> print x; print xs
>   ;;            
>
> # generic rec print = case float -> unit => print_float | $a -> unit =>
> print;;
> 
> # print 1.0;;
> 1- : unit = ()
> # print 1 (* infinite loop! *)
> 
> Well, that should be expected, since we try and print an int, which isn't
> a float, so we try (from $a -> unit => print) to print, ad
> infinitum. 

Yes, as you pointed out, your second definition of print will not
work, because its definition is completely independent from the first;
it uses the same name print, but it points to the new print. 
We can have the same thing even in the normal let rec expressions:

   let f () = print_string "hello";;
   let rec f () = f ()

The second f does not call the first f, but itself (and loops forever).

> # let print' = print ;;
>
> # generic rec print = case 
>   | float -> unit => print_float 
>   | $a -> unit => print'
>   ;;
>
> # print [1.0;2.0;3.0];; 
> This generic full instance is used with type float list -> unit
>  which is not its valid instance of ...

This does not work neither. Since what print recursively calls
is the original print, not the newly defined print. Therefore
we cannot use the interesting mixed case of float list -> unit...

> Is there some trick to build up recursive generics by parts? One thing 
> to do is to break the recursive generic into a non-recursive generic
> and a recursive function which applies the generic to the elements, 
> but that feels like cheating. Maybe open recursion for generics would 
> be desirable, so that I can add a new case and have the recursive call in 
> an existing case call the new one? Yes, it's a half baked idea, but maybe 
> a better cook can finish baking...

As we have seen, redefinitions cannot achieve what you (and I) want. 
We will need some special construction for the extension. We will have
the new "include" phrase to import the type case definitions of the
other generic values. For example:

	generic print' = case
	| include print
	| float -> unit => print_float
	;;

This newly defined print' will be semantically equivalent to the 
following generic definition:
 
	generic print' = case
	| int -> unit => print_int
	| string -> unit => print_float
	| $a list -> unit =>
	     function [] -> ()
		    | x :: xs -> print' x; print' xs  
	| float -> unit => print_float
	;;

Note that the recursive occurrences of print in the original
definition are now replaced by print', in order to point 
the newly defined generic value. In this sense, the include phrase is
not the pure inclusion of the source. Thus, the new print' can print
float lists:

      # print' [1.2; 3.4; 5.6];;
      1.23.45.6- : unit = ()

Thus, the generic values are "open" to the others who include them,
but it is not the open recursion you mentioned; print and print' are
different values and independent each other. 

  We do not want to introduce the full open recursion to generic
values. You could add new type cases easily using the open recursion
and it should be helpful in some cases. But it can also introduce
heavy semantic confusion to our programs, since in the open recursion
the semantics of generic values could not be fixed until all modules 
are linked together. 

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

Several people have courageously tried some module programming using
G'Caml and reported troubles. I am sorry but you cannot write .mli
files with extensional polymorphic values. At the time of the
implementation, we had not had clear idea to store generic constraints
in signatures... Please only use the toplevel.

I explained briefly the limitations of the current implementation at

  http://pauillac.inria.fr/~furuse/generics/

However, none of them are theoretical. 
They can be fixed when I will get some more time for coding!

Regards,

-----------------------------------------------------------------------
Jun P. Furuse 					 Jun.Furuse@inria.fr
-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

* Re: "Re: [Caml-list] A G'Caml question" + additional info
  2001-06-25 17:11 ` "Re: [Caml-list] A G'Caml question" + additional info Jun Furuse
@ 2001-06-28  2:21   ` Patrick M Doane
  2001-06-28  4:40     ` Brian Rogoff
  0 siblings, 1 reply; 4+ messages in thread
From: Patrick M Doane @ 2001-06-28  2:21 UTC (permalink / raw)
  To: Jun Furuse; +Cc: caml-list

On Mon, 25 Jun 2001, Jun Furuse wrote:

> Thus, the generic values are "open" to the others who include them,
> but it is not the open recursion you mentioned; print and print' are
> different values and independent each other. 
> 
>   We do not want to introduce the full open recursion to generic
> values. You could add new type cases easily using the open recursion
> and it should be helpful in some cases. But it can also introduce
> heavy semantic confusion to our programs, since in the open recursion
> the semantics of generic values could not be fixed until all modules 
> are linked together. 

I agree that this could make programs more difficult to follow, but I
think it is to be expected with generic programming these days.
Also, isn't this kind of semantic confusion already present with using the
object oriented system?

In the following example, writing definitions like MyProgram.print may get
very tedious in practice.

module Int = struct
  generic print = case int -> unit => print_int
end

module String = struct
  generic print = case string -> unit => print_string
end

module List = struct
  generic print =
   case $a list -> unit => function [] -> () | x :: xs -> print x; print xs
end

module MyProgram = struct
  generic print = case
  | include Int.print
  | include String.print
  | include List.print
    (*  all other print functions to follow *)
end

Given what is done with Haskell type classes and C++ templates, it seems
more confusing to me to not support open recursion.  Any thoughts?  

Patrick

-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

* Re: "Re: [Caml-list] A G'Caml question" + additional info
  2001-06-28  2:21   ` Patrick M Doane
@ 2001-06-28  4:40     ` Brian Rogoff
  0 siblings, 0 replies; 4+ messages in thread
From: Brian Rogoff @ 2001-06-28  4:40 UTC (permalink / raw)
  To: Patrick M Doane; +Cc: caml-list

On Wed, 27 Jun 2001, Patrick M Doane wrote:
> On Mon, 25 Jun 2001, Jun Furuse wrote:
> >   We do not want to introduce the full open recursion to generic
> > values. You could add new type cases easily using the open recursion
> > and it should be helpful in some cases. But it can also introduce
> > heavy semantic confusion to our programs, since in the open recursion
> > the semantics of generic values could not be fixed until all modules 
> > are linked together. 
> 
> I agree that this could make programs more difficult to follow, but I
> think it is to be expected with generic programming these days.
> Also, isn't this kind of semantic confusion already present with using the
> object oriented system?
> 
> In the following example, writing definitions like MyProgram.print may get
> very tedious in practice.
> 
> module Int = struct
>   generic print = case int -> unit => print_int
> end
> 
> module String = struct
>   generic print = case string -> unit => print_string
> end
> 
> module List = struct
>   generic print =
>    case $a list -> unit => function [] -> () | x :: xs -> print x; print xs
> end
> 
> module MyProgram = struct
>   generic print = case
>   | include Int.print
>   | include String.print
>   | include List.print
>     (*  all other print functions to follow *)
> end

Actually, that doesn't look too bad to me. Once generics play well with
modules and functors (and classes and polymorphic variants :), support the 
include syntax Jun described, and are in the release (along with a safer
marshaling) I'll be very pleased

The real issue which open recursion would solve is if you had, say, a
recursive generic for printing lists recursively, one for doing arrays
recursively, and one for doing tuples (up to some fixed arity). Here's 
how you'd write it now 

generic print_basic = case 
    | int -> unit => print_int
    | float -> unit => print_float
    | char -> unit => print_char
    | string -> unit => print_string
    | bool -> unit => 
	(function true -> print_string "true" 
         |        _ -> print_string "false")

generic rec print_seq = case 
    (* print_tuple *)
    | $a * $b -> unit => fun (u,v) -> print_seq u; print_seq v
    | $a * $b * $c -> unit => 
        fun (u,v,w) -> print_seq u; print_seq v; print_seq w
    | $a * $b * $c * $d  -> unit => 
        fun (u,v,w,x) -> 
	  (print_seq u; print_seq v; print_seq w;print_seq x)
    | $a * $b * $c * $d * $e -> unit => 
        fun (u,v,w,x,y) -> 
	   (print_seq u; print_seq v; print_seq w;print_seq x;print_seq y)
    (* print_array *)
    | $a array -> unit => fun a -> Array.iter print_seq a
    (* print_list *)
    | $a list  -> unit => fun l -> List.iter print_seq l
    | $a -> unit => print_basic;;

note that this can print a lot of stuff because of recursion:

# print_seq (1,2);;
12- : unit = ()
# print_seq (1,(2,(3,(4,(5,(6,(7,(8,9))))))));;
123456789- : unit = ()
# print_seq (1,[|(2,3);(4,5);(6,7)|],"8",9.0);;
123456789- : unit = ()

but you can't break it into parts print_tuple, print_list, print_array 
and construct print_seq by composing them. Oh well, they still give a
much needed overloading and have lots of power to spare. 

> Given what is done with Haskell type classes and C++ templates, it seems
> more confusing to me to not support open recursion.  Any thoughts?  

They're really powerful. Once the design and implementation are more
stable, we'll see how important the lack of open recursion is. I think
it's more important to have human understandable error messages and
inferred types. I'm already looking forward to the merger with OCaml
proper. 

-- Brian


-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

end of thread, other threads:[~2001-06-28  4:40 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2001-06-20  3:16 [Caml-list] A G'Caml question Brian Rogoff
2001-06-25 17:11 ` "Re: [Caml-list] A G'Caml question" + additional info Jun Furuse
2001-06-28  2:21   ` Patrick M Doane
2001-06-28  4:40     ` Brian Rogoff

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