caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* Re: [Caml-list] let rec and polymorphic functions
       [not found] <20070627100004.9E0DABC73@yquem.inria.fr>
@ 2007-06-27 10:24 ` David Allsopp
  2007-06-27 11:12   ` Jeremy Yallop
  2007-06-27 13:12   ` Jon Harrop
  0 siblings, 2 replies; 8+ messages in thread
From: David Allsopp @ 2007-06-27 10:24 UTC (permalink / raw)
  To: caml-list

> There are many problems with this. Google for ad-hoc polymorphism,
> polymorphic recursion and generic printing.
>
> On Wednesday 27 June 2007 09:40:31 David Allsopp wrote:
> >   out "TEST";
>
> val out : string -> unit
>
> >   out "%d" 0;
>
> val out : format -> int -> unit

The type-checker doesn't see that surely? Surely on that expression it sees
out : string -> int -> unit?? Note that changing the sequence to

out "%b" true;
out "%d" 0;

and removing the out "TEST" still causes problems - although O'Caml manages
to infer the [format4] first argument for [out], it fixes the first
parameter of the [format4] as being [bool -> unit] and so prevents [out]
from being used with anything other than a single "%b" argument and hence
gives a type error on the next application.

> As printf is ad-hoc polymorphic, you must supply the format specifier 
> immediately and OCaml will generate a custom printer for you. OCaml does
> not use run-time types so you cannot have a generic print function: you
> must specific print functions for each of your (possibly higher-order)
> types.

Yes, except that what's odd to me is that the type-checker doesn't behave as
the manual says it ought to with [let rec]. BUT... 

> Also, recursive calls ossify the function to a monomorphic type, so you 
> cannot do polymorphic recursion in OCaml. There are workaround using 
> recursive modules or objects but I don't think this is what you want here.

That does explain it - which I didn't know. Consider this which is also
broken (and simpler because it has nothing to do with the ad-hoc
polymorphism of printf)

let rec id x = x
and _ = id 0
in
  id (); (* *** *)
  id 1;;

Gives a type error for *** because id is already inferred as int -> int
because of the monomorphic nature of the recursive definition. This is
over-guarded but I never got an answer on a previous post as to why. The
equivalent SML does type polymorphically:

let fun id x = x
val _ = id 0
in
  id ();
  id 1
end;

Incidentally, it's lucky that this is polymorphic in SML because all
function definitions are recursive IIRC.

But no-one posted an explanation as to why there's this difference in the
let construct between the two flavours of ML :(


David


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

* Re: [Caml-list] let rec and polymorphic functions
  2007-06-27 10:24 ` [Caml-list] let rec and polymorphic functions David Allsopp
@ 2007-06-27 11:12   ` Jeremy Yallop
  2007-06-27 11:29     ` David Allsopp
  2007-06-27 12:00     ` Virgile Prevosto
  2007-06-27 13:12   ` Jon Harrop
  1 sibling, 2 replies; 8+ messages in thread
From: Jeremy Yallop @ 2007-06-27 11:12 UTC (permalink / raw)
  To: David Allsopp; +Cc: caml-list

David Allsopp wrote:
> That does explain it - which I didn't know. Consider this which is also
> broken (and simpler because it has nothing to do with the ad-hoc
> polymorphism of printf)
> 
> let rec id x = x
> and _ = id 0
> in
>   id (); (* *** *)
>   id 1;;
> 
> Gives a type error for *** because id is already inferred as int -> int
> because of the monomorphic nature of the recursive definition. 

Right.  In particular, it gives a type error because `id' is used at the 
type int -> int within its own definition (the rhs of every binding in 
the group), so that's the type that it's given for the rest of the 
program (the part following "in").

> This is
> over-guarded but I never got an answer on a previous post as to why. The
> equivalent SML does type polymorphically:
> 
> let fun id x = x
> val _ = id 0
> in
>   id ();
>   id 1
> end;

This isn't really "the equivalent SML", since the definition of `id x' 
and the application `id 0' aren't in the same recursive group.  The 
equivalent in SML would be something like

    let val rec id = fn x => x
            and _  = id 0
        in
           id ();
           id 1
    end

although this actual program isn't valid; SML only allows syntactic 
function expressions on the rhs of `let rec'.  If you throw in a "fn" to 
avoid the restriction you'll find that SML behaves essentially the same 
as OCaml:

     let val rec id = fn x => x
             and x = fn _ => id 0
         in
           id ();
           id 1
     end



OCaml seems a little inconsistent here, actually.  The application `id 
0' is only valid as the rhs of let rec because the compiler can 
determine that there's no actual recursion involved.  There doesn't seem 
to be a reason not to apply a similar analysis to type checking, 
allowing more polymorphism for functions in the same recursive group 
that aren't actually part of a cycle.

Jeremy.


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

* RE: [Caml-list] let rec and polymorphic functions
  2007-06-27 11:12   ` Jeremy Yallop
@ 2007-06-27 11:29     ` David Allsopp
  2007-06-27 12:00     ` Virgile Prevosto
  1 sibling, 0 replies; 8+ messages in thread
From: David Allsopp @ 2007-06-27 11:29 UTC (permalink / raw)
  To: OCaml List

> This isn't really "the equivalent SML", since the definition of `id x' 
> and the application `id 0' aren't in the same recursive group.  The 
> equivalent in SML would be something like

Thanks for this. I see the SML difference now - 

let fun id x = x
    and g x = id 0 + x
in
  id ()
end;

similarly doesn't work in SML with a pair of functions instead.

> OCaml seems a little inconsistent here, actually.  The application `id 
> 0' is only valid as the rhs of let rec because the compiler can 
> determine that there's no actual recursion involved.  There doesn't 
> seem to be a reason not to apply a similar analysis to type checking, 
> allowing more polymorphism for functions in the same recursive group 
> that aren't actually part of a cycle.

Which is what SML did in my "equivalent" example - and it would certainly be
nice if OCaml did the same. Is it worth raising a bug (well, feature
request) for or am I the only person who (ab)uses [let rec] in this way?

Many thanks,


David


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

* Re: [Caml-list] let rec and polymorphic functions
  2007-06-27 11:12   ` Jeremy Yallop
  2007-06-27 11:29     ` David Allsopp
@ 2007-06-27 12:00     ` Virgile Prevosto
  2007-06-27 13:00       ` Jeremy Yallop
  1 sibling, 1 reply; 8+ messages in thread
From: Virgile Prevosto @ 2007-06-27 12:00 UTC (permalink / raw)
  To: caml-list

Le mer 27 jun 2007 12:12:34 CEST,
Jeremy Yallop <jeremy.yallop@ed.ac.uk> a écrit :

> David Allsopp wrote:
> > let rec id x = x
> > and _ = id 0
> > in
> >   id (); (* *** *)
> >   id 1;;
> > 
> This isn't really "the equivalent SML", since the definition of `id
> x' and the application `id 0' aren't in the same recursive group.
> The equivalent in SML would be something like
> 
>     let val rec id = fn x => x
>             and _  = id 0
>         in
>            id ();
>            id 1
>     end
> 
> 
> OCaml seems a little inconsistent here, actually.  The application
> `id 0' is only valid as the rhs of let rec because the compiler can 

Well, it just seems that Ocaml performs type inference before checking
the validity of the recursive definition. If you give a well-typed
term, it will complain about a forbidden rhs of let rec:

        Objective Caml version 3.10.0

#  let rec id = fun x -> x and _foo = id 0 in id 1;;
This kind of expression is not allowed as right-hand side of `let rec'
-- 
E tutto per oggi, a la prossima volta.
Virgile


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

* Re: [Caml-list] let rec and polymorphic functions
  2007-06-27 12:00     ` Virgile Prevosto
@ 2007-06-27 13:00       ` Jeremy Yallop
  0 siblings, 0 replies; 8+ messages in thread
From: Jeremy Yallop @ 2007-06-27 13:00 UTC (permalink / raw)
  To: Virgile Prevosto; +Cc: caml-list

Virgile Prevosto wrote:
> Le mer 27 jun 2007 12:12:34 CEST,
> Jeremy Yallop <jeremy.yallop@ed.ac.uk> a écrit :
>> OCaml seems a little inconsistent here, actually.  The application
>> `id 0' is only valid as the rhs of let rec because the compiler can 
> 
> Well, it just seems that Ocaml performs type inference before checking
> the validity of the recursive definition. If you give a well-typed
> term, it will complain about a forbidden rhs of let rec:

You're right; I'd overestimated the extent of the analysis.  Note that 
applications on the rhs of let rec are acceptable if there's no 
recursion at all involved in the application:

   let id x = x in
   let rec f = id id
       and g x = g (f x)
    in g

Jeremy.

> 
>         Objective Caml version 3.10.0
> 
> #  let rec id = fun x -> x and _foo = id 0 in id 1;;
> This kind of expression is not allowed as right-hand side of `let rec'


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

* Re: [Caml-list] let rec and polymorphic functions
  2007-06-27 10:24 ` [Caml-list] let rec and polymorphic functions David Allsopp
  2007-06-27 11:12   ` Jeremy Yallop
@ 2007-06-27 13:12   ` Jon Harrop
  1 sibling, 0 replies; 8+ messages in thread
From: Jon Harrop @ 2007-06-27 13:12 UTC (permalink / raw)
  To: caml-list

On Wednesday 27 June 2007 11:24:53 David Allsopp wrote:
> The type-checker doesn't see that surely? Surely on that expression it sees
> out : string -> int -> unit?? Note that changing the sequence to
>
> out "%b" true;
> out "%d" 0;
>
> and removing the out "TEST" still causes problems - although O'Caml manages
> to infer the [format4] first argument for [out], it fixes the first
> parameter of the [format4] as being [bool -> unit] and so prevents [out]
> from being used with anything other than a single "%b" argument and hence
> gives a type error on the next application.

Yes. This is the polymorphic recursion part of your problem. You cannot 
call "f" polymorphically from inside the "let" binding.

Look at this:

# let rec f x = Printf.printf x and g() = f "%d" 3;;
val f : (int -> unit, out_channel, unit) format -> int -> unit = <fun>
val g : unit -> unit = <fun>

Note how "f" has been made specific to the type "int" because of its call 
inside "g".

If you separate the calls (which you can do in this special case) you recover 
the polymorphic "f":

# let rec f x = Printf.printf x;;
val f : ('a, out_channel, unit) format -> 'a = <fun>
# let g() = f "%d" 3;;
val g : unit -> unit = <fun>

The critical difference is whether or not the call to "f" appears inside its 
definition, and that includes inside the body of "g" when "g" is defined at 
the same time as "f" using "let rec". If you want more details, read papers 
about the implementation of non-generalized type variables in the 
Hindley-Milner type system.

I'll try to ossify this by example. You can do:

# let f _ = () in f 0; f "foo";;
- : unit = ()

but not:

# let rec f _ = () and g = f 0; f "foo";;
This expression has type string but is here used with type int

> > Also, recursive calls ossify the function to a monomorphic type, so you
> > cannot do polymorphic recursion in OCaml. There are workaround using
> > recursive modules or objects but I don't think this is what you want
> > here.
>
> That does explain it - which I didn't know. Consider this which is also
> broken (and simpler because it has nothing to do with the ad-hoc
> polymorphism of printf)
>
> let rec id x = x
> and _ = id 0
> in
>   id (); (* *** *)
>   id 1;;

Exactly, yes.

> Gives a type error for *** because id is already inferred as int -> int
> because of the monomorphic nature of the recursive definition. This is
> over-guarded but I never got an answer on a previous post as to why. The
> equivalent SML does type polymorphically:
>
> let fun id x = x
> val _ = id 0
> in
>   id ();
>   id 1
> end;

No, that is equivalent to the OCaml that does work:

# let f _ = () in f 0; f "foo";;
- : unit = ()

> Incidentally, it's lucky that this is polymorphic in SML because all
> function definitions are recursive IIRC.
>
> But no-one posted an explanation as to why there's this difference in the
> let construct between the two flavours of ML :(

SML does not support polymorphic recursion either:

$ sml
Standard ML of New Jersey v110.62 [built: Thu Feb 22 13:17:37 2007]
- fun f x = f 1; f 1.0; ()
val f = fn : int -> 'a
stdIn:1.16-1.21 Error: operator and operand don't agree [tycon mismatch]
  operator domain: int
  operand:         real
  in expression:
    f 1.0

The reason is basically that both OCaml and Standard ML were designed around 
the Hindley-Milner type system, which made the deliberate design decision to 
push the limits of a decideable type system as far as possible. So they were 
designed to infer as much as possible whilst ensuring that type annotations 
are never required. Polymorphic recursion oversteps this boundary.

The designers of Haskell decided to ditch decideability in favour of (much) 
more power in the type system. So Haskell sometimes requires you to write 
explicit type annotations. Indeed, Haskell programmers almost always write 
type annotations, which is one of the reasons why idiomatic Haskell is 
comparatively verbose.

-- 
Dr Jon D Harrop, Flying Frog Consultancy Ltd.
The OCaml Journal
http://www.ffconsultancy.com/products/ocaml_journal/?e


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

* Re: [Caml-list] let rec and polymorphic functions
  2007-06-27  9:05 ` [Caml-list] " Jon Harrop
@ 2007-06-27 10:14   ` Arnaud Spiwack
  0 siblings, 0 replies; 8+ messages in thread
From: Arnaud Spiwack @ 2007-06-27 10:14 UTC (permalink / raw)
  To: caml-list

Jon Harrop a écrit :
> There are many problems with this. Google for ad-hoc polymorphism, polymorphic 
> recursion and generic printing.
>
> On Wednesday 27 June 2007 09:40:31 David Allsopp wrote:
>   
>>   out "TEST";
>>     
>
> val out : string -> unit
>   
Actually it seems to infer properly "out : (unit, out_channel, unit) 
format -> unit". So the magic is pulled here (which surprises me a lot, 
but well). The problem seems more related to the fact that mutual 
recursive function are monomorphic.
>   
>>   out "%d" 0;
>>     
>
> val out : format -> int -> unit
>
> As printf is ad-hoc polymorphic, you must supply the format specifier 
> immediately and OCaml will generate a custom printer for you. OCaml does not 
> use run-time types so you cannot have a generic print function: you must 
> specific print functions for each of your (possibly higher-order) types.
>
> Also, recursive calls ossify the function to a monomorphic type, so you cannot 
> do polymorphic recursion in OCaml. There are workaround using recursive 
> modules or objects but I don't think this is what you want here.
>
>   


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

* Re: [Caml-list] let rec and polymorphic functions
  2007-06-27  8:40 David Allsopp
@ 2007-06-27  9:05 ` Jon Harrop
  2007-06-27 10:14   ` Arnaud Spiwack
  0 siblings, 1 reply; 8+ messages in thread
From: Jon Harrop @ 2007-06-27  9:05 UTC (permalink / raw)
  To: caml-list


There are many problems with this. Google for ad-hoc polymorphism, polymorphic 
recursion and generic printing.

On Wednesday 27 June 2007 09:40:31 David Allsopp wrote:
>   out "TEST";

val out : string -> unit

>   out "%d" 0;

val out : format -> int -> unit

As printf is ad-hoc polymorphic, you must supply the format specifier 
immediately and OCaml will generate a custom printer for you. OCaml does not 
use run-time types so you cannot have a generic print function: you must 
specific print functions for each of your (possibly higher-order) types.

Also, recursive calls ossify the function to a monomorphic type, so you cannot 
do polymorphic recursion in OCaml. There are workaround using recursive 
modules or objects but I don't think this is what you want here.

-- 
Dr Jon D Harrop, Flying Frog Consultancy Ltd.
The OCaml Journal
http://www.ffconsultancy.com/products/ocaml_journal/?e


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

end of thread, other threads:[~2007-06-27 13:18 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20070627100004.9E0DABC73@yquem.inria.fr>
2007-06-27 10:24 ` [Caml-list] let rec and polymorphic functions David Allsopp
2007-06-27 11:12   ` Jeremy Yallop
2007-06-27 11:29     ` David Allsopp
2007-06-27 12:00     ` Virgile Prevosto
2007-06-27 13:00       ` Jeremy Yallop
2007-06-27 13:12   ` Jon Harrop
2007-06-27  8:40 David Allsopp
2007-06-27  9:05 ` [Caml-list] " Jon Harrop
2007-06-27 10:14   ` Arnaud Spiwack

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