caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] limits on mutual recursion and modules?
@ 2001-11-22  1:53 William Harold Newman
  2001-11-22  4:27 ` Brian Rogoff
  0 siblings, 1 reply; 4+ messages in thread
From: William Harold Newman @ 2001-11-22  1:53 UTC (permalink / raw)
  To: caml-list

I'm trying to understand the limits on mutual recursion in ML.

I've seen the hack
  type 'a combination = T1 of int | T2 of 'a | T3 of 'a * 'a
  class virtual test =
    object
      method virtual get: test combination
    end
for mutual recursion between classes and modules in OCaml. That doesn't 
leave me with much confidence that I can figure out whether a particular
kind of mutual recursion is possible.:-|

I've seen various statements about recursion between modules being
impossible, but I'm not sure exactly how severe a limitation this is
in practice, especially given the possibility of hacks like the one
above.

In particular, I'm curious whether it's possible to define
a record type Foo which contains a functor-defined data structure which 
refer to objects of type Foo. E.g., in OCaml is there any way
to define a record type Foo one of whose fields is a Set of Foo?
If so, how? If not,
  * What's the recommended way to represent nontrivial interactions 
    between an object and other objects of the same type?
  * Is it possible to do this in any other strongly typed functional
    languages? And if not,
    ** Why not? Is it known to lead to undecidability or other
       horrendous problems, or is it not considered important, or is
       it an unsolved problem, or...

In general, I'd be interested in any pointers to treatments of this
problem and the theoretical limits involved. (Also the design
decisions involved: Why use "let rec" to combine functions and
"type ... and ..." to combine types and "class ... and ..." to combine
classes and so forth instead of some "rec ... end" or
"struct rec ... end" construct to make everything inside
mutually recursive?) I've spent some time with search engines and
"mutual recursion" and "recursion between modules" and "ml" and
"ocaml" and so forth, but I haven't stumbled on anything that really
nails it.

-- 
William Harold Newman <william.newman@airmail.net>
"Furious activity is no substitute for understanding." -- H. H. Williams
PGP key fingerprint 85 CE 1C BA 79 8D 51 8C  B9 25 FB EE E0 C3 E5 7C
-------------------
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] limits on mutual recursion and modules?
  2001-11-22  1:53 [Caml-list] limits on mutual recursion and modules? William Harold Newman
@ 2001-11-22  4:27 ` Brian Rogoff
  2001-11-22  4:40   ` William Harold Newman
  0 siblings, 1 reply; 4+ messages in thread
From: Brian Rogoff @ 2001-11-22  4:27 UTC (permalink / raw)
  To: William Harold Newman; +Cc: caml-list

On Wed, 21 Nov 2001, William Harold Newman wrote:
> I'm trying to understand the limits on mutual recursion in ML.
> 
> I've seen the hack
>   type 'a combination = T1 of int | T2 of 'a | T3 of 'a * 'a
>   class virtual test =
>     object
>       method virtual get: test combination
>     end
> for mutual recursion between classes and modules in OCaml. That doesn't 

You mean between classes and types here, of course. 

> leave me with much confidence that I can figure out whether a particular
> kind of mutual recursion is possible.:-|
> 
> I've seen various statements about recursion between modules being
> impossible, but I'm not sure exactly how severe a limitation this is
> in practice, especially given the possibility of hacks like the one
> above.

That hack, which I've seen called the "parameterization trick" (we really
need a better, sexier sounding name for it) is the way you currently 
create a recursion between a type definition and a functor instantiation.

> In particular, I'm curious whether it's possible to define
> a record type Foo which contains a functor-defined data structure which 
> refer to objects of type Foo. E.g., in OCaml is there any way
> to define a record type Foo one of whose fields is a Set of Foo?

You use that same trick. It also means that you must make a polymorphic
version of Set to participate in the recursion; the library Set won't do. 
Check this out 

http://caml.inria.fr/archives/200010/msg00154.html

It's my guess that every frequent user of OCaml or SML bangs into this
within their first 9 months of serious ML programming, and most likely
long before that.

> In general, I'd be interested in any pointers to treatments of this
> problem and the theoretical limits involved. 

http://cristal.inria.fr/~hirschow/index.html

as he is the one working on it and he has pointers to related work there. 

-- 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] limits on mutual recursion and modules?
  2001-11-22  4:27 ` Brian Rogoff
@ 2001-11-22  4:40   ` William Harold Newman
  0 siblings, 0 replies; 4+ messages in thread
From: William Harold Newman @ 2001-11-22  4:40 UTC (permalink / raw)
  To: caml-list

On Thu, Nov 22, 2001 at 04:27:24AM +0000, Brian Rogoff wrote:
> On Wed, 21 Nov 2001, William Harold Newman wrote:

> > for mutual recursion between classes and modules in OCaml. That doesn't 
> 
> You mean between classes and types here, of course. 

Yes.

> You use that same trick. It also means that you must make a polymorphic
> version of Set to participate in the recursion; the library Set won't do. 
> Check this out 
> 
> http://caml.inria.fr/archives/200010/msg00154.html

Thank you, that seems to be exactly what I was looking for.

> It's my guess that every frequent user of OCaml or SML bangs into this
> within their first 9 months of serious ML programming, and most likely
> long before that.

It might make a good entry in <http://caml.inria.fr/FAQ/FAQ_EXPERT-eng.html>.

-- 
William Harold Newman <william.newman@airmail.net>
"Furious activity is no substitute for understanding." -- H. H. Williams
PGP key fingerprint 85 CE 1C BA 79 8D 51 8C  B9 25 FB EE E0 C3 E5 7C
-------------------
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] limits on mutual recursion and modules?
@ 2001-11-29 20:04 Tom Hirschowitz
  0 siblings, 0 replies; 4+ messages in thread
From: Tom Hirschowitz @ 2001-11-29 20:04 UTC (permalink / raw)
  To: caml-list



 > I'm interested, and I'm sure the rest of the list will be interested too.

Allright, I'll try. 
People who are not interested by type theory for recursive modules may stop 
here.

Warning
-------
I am far from understanding all the details of what I will
write. If people are interested in understanding better, we can try.
Anyway, I'll drop some questions here and there, and any hints are welcome.

Preliminaries 1 : phase splitting
---------------------------------
In [1, 2, 3], the authors consider languages that are quite far from
the concrete syntax, in the sense that type and value components of modules
may always be separated, thanks to phase-splitting rules, and in the sense 
that sub-modules are merged in the top level structure. 
This was first done in [2], once for all.
So when we try to understand what happens to a program like 

module rec M (X : sig
		module A : sig type t val f : t -> t end
		module B : sig type t val g : t -> t end
	      end) = struct
  module A = struct 
    type t = string
    let f x = x ^ "!"
  end
  module B = struct
    type t = int
    let g n = (n + 1)
  end
end

in their systems, we obtain something like

fix (X : [ 'a : (Type * Type). (('a.1 -> 'a.1)
                              * ('a.2 -> 'a.2)) ]) =
[ <string, int>, 
  (\x . x ^ "!",
   \n . n + 1) ]

where 'a represents the set of type components of the forwardly declared module X.

Indeed a module is split into type components and value components, and is 
therefore of the form 

  M ::= s        (* identifier *) 
      | [ c, e ] (* type constructor, expression *)

while a signature is of the form 

  S ::= [ 'a : K. sigma ] (* type variable 'a of a certain kind K
                             plus a value type simga *)

This has the advantage that forward dependencies of value components 
on type components are no longer forward in their calculus.
For example, see the following recursive signature :

sig rec (X : sig module A : sig type t end
                 module B : sig type t end) = sig
  module A : sig
    type t = ...
    val f : X.B.t -> int
  end
  module B : sig
    type t 
end

It becomes

sig rec (s : [ 'a : (Type * Type) , nothing ]) = 
[ 'b : (Type * Type) ,
  (Fst s).2 -> int ]
  ~~~~~~~~~
(underlined the X.B.t)

which is simply equivalent to 

[ 'a : (Type * Type) ,
  'a.2 -> int ]

I agree that this simplifies problems, but it makes it hard 
to imagine what they would look like in the real language, 
and I ended up not really understanding what each version 
exactly does. By the way, I think it is mentioned somewhere 
that it was difficult to adapt the theory to non-split modules.

Preliminaries 2 : equi-recursive type constructors
--------------------------------------------------
Somewhere at the base of [1, 3] lies the notion of equi-recursive
type constructors. 
This means that the grammar for type constructors has a "mu" entry
of the form 

  c ::= ... | mu('a). c

and that in the equational theory on types, for all c (modulo some
side-conditions), 

    mu('a). c = c { (mu('a). c) / 'a }

The problem is that this is an equality of the theory, not an
isomorphism (ie for example, there are no roll and unroll operations
needed). And this makes unification of type constructors undecidable
(or nobody knows, maybe).

Now, there are several attempts in [3] for systems with recursive modules.

1. Opaque recursive modules, version CHP
----------------------------------------
The first system exposed in [1].
The simplest one : add a construct fix (X : S) M to the module language and 
type the body of the recursive module under the assumption that the forward
variable is of the declared signature. The body must be of the same signature.
The rule is basically (dropping the valuability conditions to avoid bad cycles)

    s fresh   C[s : S] |- M : S    C |- S sig
   ------------------------------------------
            C |- fix(s : S).M : S

Already here, there is a question. The authors mention that "making S opaque is not 
an abstraction mechanism", but I honestly don't see why.

Drawback : not very expressive. The list example is not well-typed : 

module type LIST = sig 
  type t 
  val nil : t
  val cons : int * t -> t
end

module rec List = fix (X : LIST)
  type t = unit + int * X.t
  let nil = inl ()
  let cons (x, l) = inr (x, l)
end

In the body of List, we have cons : int * List.t -> t, 
instead of the required int * t -> t.

Of course, this is a bit of a false example, because one could write 
the known List module directly, but it is theirs.
The problem seems to be that X.t and t are not identified in the body.

Finally, there seems to be an issue that [1] had not mentioned. The 
system makes use of equi-recursive type constructors, which makes it
probably undecidable, so the high-level language should not allow 
the user to mention them, letting the system deal with them alone.
This is quite unclear though.

2. Transparent recursive modules version CHP
--------------------------------------------

For me this is the closest to Russo's system, although the authors 
seem to consider that they are very different.

There is a notion of recursively dependent signature (rds), which is 
quite similar to Russo's. 
This solves the List example by declaring 

module type rec LIST' (X : sig type t end) = 
  type t = unit + int * X.t
  ...
end

module rec List (X : LIST') = 
  type t = unit + int * X.t
  ...
end

so that in the body, we have 
 X.t = unit + int * X.t (because the rds is transparent).

The user is not allowed to manipulate equi-recursive type constructors, 
except within rds and fixpoint modules, which encapsulate recursivity.
To compare this one with 1., 2. is clearly more expressive.
However, both make use of equi-recursive type constructors, 
so the real purpose of [3] seems to avoid this.

3. Opaque fixpoint modules, version DHC
---------------------------------------
This is a hack to solve the List example.
It consists in inferring for the body a signature that may be
different from the forwardly declared one, provided that type 
components have the same kind and that replacing forward references 
in it with the corresponding ones in the forward yield an equivalent signature.
Technically, the typing rule is 

  'a, s fresh
  C |- S = [ 'a : K. sigma1 ]
  C[s : S] |- M : [ 'a : K. sigma2 ]
  C['a : K] |- sigma1 = (sigma2 { 'a / (Fst s) })
  -----------------------------------------------
  C |- fix(s : S) M : S

However, it does not solve similar examples where there are forward
references to types.
Indeed, we are not able to define a sufficiently precise signature.
For example :

module type EXPDEC = sig
  module Exp : sig
    type exp
    val make_let : Dec.dec * exp -> exp
  end
  module Dec : sig
    type dec
    val make_val : identifier * Exp.exp -> dec
  end
end

is not well-formed, because of the forward reference to Dec.dec, and
there doesn't seem to be any way of specifying what we want here,
still identifying the type of the first argument of make_let with
Dec.dec. 

4. Opaque RDS's
---------------
Yet another construct, which is defined 
for using in 3. to deal with this problem.
It is similar to first rds, except that in contrast, it must not 
show to the outside any recursive dependency.
The rule is 

  'a, 'b fresh 
  C |- K kind
  C['b : K] |- S = [ 'a : K. sigma ]
  ----------------------------------
  C |- rec('b).S

and the restriction on dependencies comes from the fact that 
K must be well-formed under the ambient context, thus without forward
references. Sor for the ExpDec example, one would declare the
following opaque rds :

rec(ExpDec). sig
  module Exp : sig
    type exp
    val make_let : ExpDec.Dec.dec * exp -> exp
  end
  module Dec : sig
    type dec
    val make_val : identifier * Exp.exp -> dec
  end
end

This way, it seems that both difficult examples would work.
Remark :
the recursive modules system consituted by opaque fixpoint modules and 
opaque rds's only uses equi-recursiveness in typechecking fixpoint
modules, not for rds's.

5. Transparent fixed-point modules, version [3]
-----------------------------------------------
This is a subtle refinment of 2.
In 2., both the rules for rds checking and fixpoint module typing 
required equi-recursive type constructors, but by restricting fixpoint 
modules to transparent signatures, the authors notice that the
epxressiveness is retained, while equi-recursiveness is not necessary 
for fixpoint module typing, and remains only for rds checking.
So it amounts to localizing equi-recursiveness to transparent rds's.

Conclusion
----------

This is the first part of the paper, and the conclusions are basically
that the two systems one should really consider are
- opaque fixpoint modules and opaque rds's and
- transparent fixpoint modules and transparent rds's.
However, the use of equi-recursiveness makes them not suitable for
practical implementation, and the second part tries to deal with that.

I did not read that second part yet. However, thanks to Brian who
forced me to dive again into this terrible paper. I understood much better this
time than before (imagine how it was)! Maybe, it will encourage me to
read on.

Questions 
---------

a. Why wouldn't Russo's extension suffer from equi-recursiveness too?
(think the answer is in the second part somewhere)

b. How exactly is this compatible with separate compilation (cf
Russos's problems)?
(there a section about that)

[1] Crary, Harper, Puri. What is a recursive module?
[2] Harper, Mitchell, Moggi. Higher-order modules and the phase distinction.
[3] Dreyer, Harper, Crary. Toward a practical type theory for recursive modules.
-------------------
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-11-29 19:03 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2001-11-22  1:53 [Caml-list] limits on mutual recursion and modules? William Harold Newman
2001-11-22  4:27 ` Brian Rogoff
2001-11-22  4:40   ` William Harold Newman
2001-11-29 20:04 Tom Hirschowitz

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