Search code examples
ats

Why can't I call the second half of a mutually recursive function that's defined locally?


As a follow up to Why can't generic function templates use fnx to ensure they are tail-recursive?, and after reading Compiler Friendly Tail Recursion + Tail Recursion Checking in ATS, I wanted to see if i could ensure my function either got tail-call optimized, or failed to compile. This is the function:

fun {a:t@ype} repeat {n:nat} .<n>.
( x: a
, t: int n
, f: a -> a
) : a =
  if t = 0 then x
  else repeat (f x, t - 1, f)

So first I made it a trivial mutually recursive function, like so:

fnx {a:t@ype} repeat1 {n:nat} .<n>.
( x: a
, t: int n
, f: a -> a
) : a =
  if t = 0 then x
  else repeat1 (f x, t - 1, f)
and {a:t@ype} repeat2 {n:nat} .<>.
( x: a
, t: int n
, f: a -> a
) : a = repeat1 (x, t, f)

But that doesn't compile, it seems like it doesn't like the generic type arguments. So I put the definition inside a third parent function, which has the generic argument and just calls into the mutual recursive pair.

fun {a:t@ype} repeat {n:nat} .<n>.
( x: a
, t: int n
, f: a -> a
) : a =
let
  fnx repeat1 {n:nat} .<n>.
  ( x: a
  , t: int n
  , f: a -> a
  ) : a =
    if t = 0 then x
    else repeat1 (f x, t - 1, f)
  and repeat2 {n:nat} .<>.
  ( x: a
  , t: int n
  , f: a -> a
  ) : a = repeat1 (x, t, f)
in
  repeat1 (x, t, f)
end

Viola, it compiles! But then I tried calling repeat2 instead of repeat1 in the last line, like so:

fun {a:t@ype} repeat {n:nat} .<n>.
  //snip...
in
  repeat2 (x, t, f)
end

And this fails to compile, with error:

the dynamic identifier [repeat2] is unrecognized.

Hence, the question in the title.


Solution

  • When you use 'fnx' to define mutually recursive functions, only the first defined function is available for subsequent use.