From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Original-To: caml-list@sympa.inria.fr Delivered-To: caml-list@sympa.inria.fr Received: from mail2-relais-roc.national.inria.fr (mail2-relais-roc.national.inria.fr [192.134.164.83]) by sympa.inria.fr (Postfix) with ESMTPS id 2C26E7F75C for ; Thu, 11 Sep 2014 01:16:59 +0200 (CEST) Received-SPF: None (mail2-smtp-roc.national.inria.fr: no sender authenticity information available from domain of yotambarnoy@gmail.com) identity=pra; client-ip=209.85.192.45; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="yotambarnoy@gmail.com"; x-sender="yotambarnoy@gmail.com"; x-conformance=sidf_compatible Received-SPF: Pass (mail2-smtp-roc.national.inria.fr: domain of yotambarnoy@gmail.com designates 209.85.192.45 as permitted sender) identity=mailfrom; client-ip=209.85.192.45; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="yotambarnoy@gmail.com"; x-sender="yotambarnoy@gmail.com"; x-conformance=sidf_compatible; x-record-type="v=spf1" Received-SPF: None (mail2-smtp-roc.national.inria.fr: no sender authenticity information available from domain of postmaster@mail-qg0-f45.google.com) identity=helo; client-ip=209.85.192.45; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="yotambarnoy@gmail.com"; x-sender="postmaster@mail-qg0-f45.google.com"; x-conformance=sidf_compatible X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: Aj8BAEvbEFTRVcAtm2dsb2JhbABgg2BbgnjFYYFph00BgQsIFhABAQEBAQYLCwkUKoQDAQEBAwESEQQZARsdAQMBCwYDAgsNKgICIQEBEQEFARwGEyKICwEDCQgNjTmQKmuLMIFygxCJAQoZJw1mhXwBEQEFDo0Sgi0HgnmBUwEElXmEc4IQgV+NHYRLGCmFLiEvAYJOAQEB X-IPAS-Result: Aj8BAEvbEFTRVcAtm2dsb2JhbABgg2BbgnjFYYFph00BgQsIFhABAQEBAQYLCwkUKoQDAQEBAwESEQQZARsdAQMBCwYDAgsNKgICIQEBEQEFARwGEyKICwEDCQgNjTmQKmuLMIFygxCJAQoZJw1mhXwBEQEFDo0Sgi0HgnmBUwEElXmEc4IQgV+NHYRLGCmFLiEvAYJOAQEB X-IronPort-AV: E=Sophos;i="5.04,502,1406584800"; d="scan'208";a="94038145" Received: from mail-qg0-f45.google.com ([209.85.192.45]) by mail2-smtp-roc.national.inria.fr with ESMTP/TLS/RC4-SHA; 11 Sep 2014 01:16:58 +0200 Received: by mail-qg0-f45.google.com with SMTP id j107so6612391qga.4 for ; Wed, 10 Sep 2014 16:16:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc:content-type; bh=87CAskQvgzX0xV0xD6IQjeoJnHJwppR1Q1irDxrKg/A=; b=blH8QP1LTp+2qzl1fDCbRvLHO0Oc5vp4CsKztgen0Yhx9hZA1u0NzQUX1+cZ1I9gGV zOwCiKZsZJZTEiPg+MecPuIIAqeey0RpG2nj1mEYZNEEGwW0TU1Uh6eylQvYvWqgv2eX 8LXtO28Ki+eOHvQsCHG0hDXJ5KjzKGStdRgQsz9b356VLcrxcsBvmdssZgozCx5OMafh pBdN0IQHzQxl6ydo+BkHmfpAKH9t7EaKq5yI0dxRFfAJ+gT6Sm267Kr6S+oiXksSQFBe mgR0gnMyi7U7x1VcYCe9TwfXCfkADNm/0bne1Y86UMOpb677Qz+EtfhOdwW7NUpdWaxE vfHg== X-Received: by 10.224.8.131 with SMTP id h3mr8556385qah.35.1410391017219; Wed, 10 Sep 2014 16:16:57 -0700 (PDT) MIME-Version: 1.0 Received: by 10.224.174.68 with HTTP; Wed, 10 Sep 2014 16:16:37 -0700 (PDT) In-Reply-To: References: From: Yotam Barnoy Date: Wed, 10 Sep 2014 19:16:37 -0400 Message-ID: To: Jeremy Yallop Cc: Ocaml Mailing List Content-Type: multipart/alternative; boundary=001a11c2be64936ec80502be3f73 Subject: Re: [Caml-list] Undefined recursive module --001a11c2be64936ec80502be3f73 Content-Type: text/plain; charset=UTF-8 Thank you for the detailed explanation. That makes sense, as much as I wish there was a static detection mechanism in place for this kind of thing. On Wed, Sep 10, 2014 at 6:50 PM, Jeremy Yallop wrote: > On 10 September 2014 23:26, Yotam Barnoy wrote: > > On Wed, Sep 10, 2014 at 6:20 PM, Jeremy Yallop wrote: > >> > >> On 10 September 2014 21:32, Yotam Barnoy wrote: > >> > I just encountered this nasty RUNTIME error in my code. What does it > >> > mean? > >> > How does it happen? > >> > >> The behaviour is documented in the manual: > >> > >> http://caml.inria.fr/pub/docs/manual-ocaml-400/manual021.html#toc75 > >> > >> See the paragraph beginning "Currently, the compiler requires [...]". > > > > Thanks. Does it make sense that changing a function definition from > > point-free to an explicit definition should eliminate this exception? > > Yes, that's the expected behaviour. Initially the fields in a > recursive module are bound to functions that raise an exception. The > module initialization then overwrites each field with its actual > value, which is determined by evaluating the expression on the right > hand side of the field definition. If evaluating the right hand side > involves reading one of the fields of the module that has not yet been > overwritten then the field will resolve to the exception-raising > function, which may lead to the runtime error that you've seen. > > Here's an example. Suppose you have a recursive module like this: > > module rec M1 > : sig val x : unit -> unit end = > struct > let x = M1.x > end > > Then the initial state of the module at runtime has x bound to a > function that raises an exception: > > module rec M1 > : sig val x : unit -> unit end = > struct > let x = fun _ -> raise Undefined_recursive_module > end > > Module initialization then overwrites the field with the result of > evaluating the right-hand side -- that is, by the result of evaluating > M1.x: > > M1.x <- (fun _ -> raise Undefined_recursive_module) > > Calling M1.x will lead to the exception being raised: > > M1.x () > => raise Undefined_recursive_module > > Now consider what happens when you eta-expand the definition of x. > Here's the source program > > module rec M2 : sig val x : unit -> unit end = > struct > let x = fun e -> M2.x e > end > > The initial state of the module at runtime is the same as for M1: > > module rec M2 : sig val x : unit -> unit end = > struct > let x = fun _ -> raise Undefined_recursive_module end > end > > Once again, module initialization overwrites the field with the result > of evaluating the right-hand side. This time, however, evaluating the > right-hand side doesn't require resolving M2.x, since M2.x is under a > 'fun' binding: > > M2.x <- (fun e -> M2.x e) > > When you come to call M2.x the recursive reference resolves to the new > value of the field and evaluation proceeds as expected: > > M2.x () > => M2.x () > > As the manual says, all of this is subject to change, and it's best > not to rely on the current behaviour. I recommend that you avoid > using recursive modules for value-level recursion, if possible. > --001a11c2be64936ec80502be3f73 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
Thank you for the detailed explanation. That makes sense, = as much as I wish there was a static detection mechanism in place for this = kind of thing.

On Wed, Sep 10, 2014 at 6:50 PM, Jeremy Yallop <yallop@gmail.com>= wrote:
On 10 Sep= tember 2014 23:26, Yotam Barnoy <yotambarnoy@gmail.com> wrote:
> On Wed, Sep 10, 2014 at 6:20 PM, Jeremy Yallop <yallop@gmail.com> wrote:
>>
>> On 10 September 2014 21:32, Yotam Barnoy <yotambarnoy@gmail.com> wrote:
>> > I just encountered this nasty RUNTIME error in my code. What = does it
>> > mean?
>> > How does it happen?
>>
>> The behaviour is documented in the manual:
>>
>>=C2=A0 =C2=A0 http://caml.inria.fr/pub/docs/= manual-ocaml-400/manual021.html#toc75
>>
>> See the paragraph beginning "Currently, the compiler requires= [...]".
>
> Thanks. Does it make sense that changing a fun= ction definition from
> point-free to an explicit definition should eliminate this exception?<= br>
Yes, that's the expected behaviour.=C2=A0 Initially the fields i= n a
recursive module are bound to functions that raise an exception.=C2=A0 The<= br> module initialization then overwrites each field with its actual
value, which is determined by evaluating the expression on the right
hand side of the field definition.=C2=A0 If evaluating the right hand side<= br> involves reading one of the fields of the module that has not yet been
overwritten then the field will resolve to the exception-raising
function, which may lead to the runtime error that you've seen.

Here's an example.=C2=A0 Suppose you have a recursive module like this:=

=C2=A0 module rec M1
=C2=A0 =C2=A0 : sig val x : unit -> unit end =3D
=C2=A0 struct
=C2=A0 =C2=A0 =C2=A0 let x =3D M1.x
=C2=A0 end

Then the initial state of the module at runtime has x bound to a
function that raises an exception:

=C2=A0 =C2=A0module rec M1
=C2=A0 =C2=A0 =C2=A0: sig val x : unit -> unit end =3D
=C2=A0 =C2=A0struct
=C2=A0 =C2=A0 =C2=A0 =C2=A0let x =3D fun _ -> raise Undefined_recursive_= module
=C2=A0 =C2=A0end

Module initialization then overwrites the field with the result of
evaluating the right-hand side -- that is, by the result of evaluating
M1.x:

=C2=A0 =C2=A0M1.x <- (fun _ -> raise Undefined_recursive_module)

Calling M1.x will lead to the exception being raised:

=C2=A0 =C2=A0M1.x ()
=C2=A0 =C2=A0 =C2=A0=3D> raise Undefined_recursive_module

Now consider what happens when you eta-expand the definition of x.
Here's the source program

=C2=A0 =C2=A0module rec M2 : sig val x : unit -> unit end =3D
=C2=A0 =C2=A0struct
=C2=A0 =C2=A0 =C2=A0 =C2=A0let x =3D fun e -> M2.x e
=C2=A0 =C2=A0end

The initial state of the module at runtime is the same as for M1:

=C2=A0 =C2=A0module rec M2 : sig val x : unit -> unit end =3D
=C2=A0 =C2=A0struct
=C2=A0 =C2=A0 =C2=A0 =C2=A0let x =3D fun _ -> raise Undefined_recursive_= module end
=C2=A0 =C2=A0end

Once again, module initialization overwrites the field with the result
of evaluating the right-hand side.=C2=A0 This time, however, evaluating the=
right-hand side doesn't require resolving M2.x, since M2.x is under a 'fun' binding:

=C2=A0 =C2=A0M2.x <- (fun e -> M2.x e)

When you come to call M2.x the recursive reference resolves to the new
value of the field and evaluation proceeds as expected:

=C2=A0 =C2=A0M2.x ()
=C2=A0 =C2=A0 =C2=A0=3D> M2.x ()

As the manual says, all of this is subject to change, and it's best
not to rely on the current behaviour.=C2=A0 I recommend that you avoid
using recursive modules for value-level recursion, if possible.

--001a11c2be64936ec80502be3f73--