
What is “Open Recursion”? - jasim
http://journal.stuffwithstuff.com/2013/08/26/what-is-open-recursion/
======
clusmore
I'm a little confused by the summary. To me it looks like he's showed that you
_can_ simulate open recursion with only structures and functions.

If it's because doing so requires forward-declarations/hoisting/reassignment,
here[1] is an implementation in JavaScript that has only a single `let`
statement, for the counter itself.

[1] [https://jsfiddle.net/n2c3s7r5/](https://jsfiddle.net/n2c3s7r5/)

~~~
munificent
I could be wrong, but I think your mutualRecursion() function is a multi-
parameter version of the Y combinator, which is the classic way of introducing
recursion to a language that doesn't natively support it. If so then, yes,
that works.

~~~
clusmore
> but I think your mutualRecursion() function is a multi-parameter version of
> the Y combinator

Yes, exactly. I've created a fork [1] where I replace the multiple-parameter Y
combinator with a single-parameter kind that operates on what is effectively a
vtable.

[1] [https://jsfiddle.net/9ump7bt5/1/](https://jsfiddle.net/9ump7bt5/1/)

------
Animats
_Pierce ... coined “open recursion” to refer to the kind of extensions you
need to build an OOP language from a non-OOP one that just has functions (i.e.
“lambdas”, “closures”, or “anonymous delegates”) and records (more or less
“object literals” in JS or “maps” in other languages)._

Which has nothing to do with recursion.

A lambda and a closure are completely different things. A lambda is just an
anonymous function. A closure is a function instance which carries with it
some context from an outer scope. You can have a lambda which is not a
closure, and a closure which is not a lambda. Javascript often has closures
which are not lambdas - that's the usual form of a callback function.

For languages that work much like LISP, with dynamic function creation, nested
functions, and garbage collection, you get most of the heavy machinery needed
for closures by default. If you've got that, you can kludge objects into
existence. This was originally called "flavors" in LISP. Javascript does this,
and suffers from having too many ways to create OOP-like objects. The LISP and
Javascript experiences indicate that implementing OOP via closures creates a
mess in source code.

Lambdas are just syntactic sugar for nested functions.

~~~
wickawic
> Which has nothing to do with recursion.

Had you read the article you might have realized this term is overloaded.

~~~
Animats
_“When I use a word,” Humpty Dumpty said, in rather a scornful tone, “it means
just what I choose it to mean—neither more nor less.” “The question is,” said
Alice, “whether you can make words mean so many different things.” “The
question is,” said Humpty Dumpty, “which is to be master—that 's all.”_ \-
Lewis Carroll, a mathematician who wrote stories in his spare time.

~~~
Tarean
Just because you didn't happen to not know this one doesn't mean that anyone
just invented a new meaning.

[https://en.m.wikipedia.org/wiki/This_(computer_programming)#...](https://en.m.wikipedia.org/wiki/This_\(computer_programming\)#Open_recursion)

------
c517402
This article is great and IMHO is an example of the internet at its best. The
article author, who knew and understood the intellectual development of the
book where 'open recursion' was first used, explained, in the context of the
book, exactly what the book author meant. Subsequently, we get the internet at
less than its best when commenters quibble and squabble over terminology.

Read the article and ignore the comments.

------
jfoutz
That's pretty neat, but imagine how cool it would be if there was some sort of
function that would take a lambda as an argument, and then call that lambda
with itself as the first argument! sort of split the lambda into a Y and
combine it with itself.

I have a pretty good idea of what a scheme implementation is, but i'll leave
the details as an exercise for the reader.

~~~
Sean1708
Do you mean like this?

    
    
      In [1]: def foo(f, a):
         ...:     return f(foo, a)
         ...: 
    
      In [2]: foo(lambda f, a: f(lambda f, a: a, a-1) if a > 0 else a, 10)
      Out[2]: 9
    

Obviously that's a completely useless and convoluted example, but I'm fairly
sure the concept of a lambda calling the function it was passed to just works
in most languages.

~~~
jfoutz
it was a stupid joke about the y combinator. you can arrange a version with
more function arguments that'll call all of the parts with their lambda
friends. so they can all call each other.

so something vaguely like

    
    
       (define local-state)
       (super-y
          (lambda (inc get set v) 
              (set inc get set (+ 1(get inc get set v))))) ;this one is inc
          (lambda (inc get set v) local-state) ;this one is get
          (lambda (inc get set v) (set! local-state v)) ; this one is set
    

you don't really even need local state, just pass around an accumulator.

Once you have lambdas and a system that allows infinite types, mutual
visibility is pretty easy to come by, mutual anonymous recursion is something
i think is super nifty.

you can save yourself some pain by sticking the functions in a map, of course
and passing that around instead.

------
LeoNatan25
“The problem is that increment is calling get and set here, but those
functions haven’t been declared yet. Unlike JavaScript, Dart doesn’t silently
hoist function declarations up.”

Huh? What is this, the 80s?

~~~
Scarbutt
FWIW, Clojure also by default doesn't let you use functions before they are
declared but gives you a construct to do it explicitly, which I haven't seen
much in the wild. And since Clojure doesn't have full TCO I guess its use
won't rise either.

~~~
JadeNB
> FWIW, Clojure also by default doesn't let you use functions before they are
> declared but gives you a construct to do it explicitly, which I haven't seen
> much in the wild. And since Clojure doesn't have full TCO I guess its use
> won't rise either.

Honest question: what does TCO have to do with use-before-declaration? (Maybe
you mean that self-calls—not the only possible kind of tail call!—aren't
natively allowed? I don't use Clojure, so I don't know if that's the case.)

~~~
Scarbutt
_Honest question: what does TCO have to do with use-before-declaration?_

Mutual recursion, tails calls to other functions. If you have two functions
that each one calls each other you get a compiler error cause one of the two
has not seen the definition of the other.

Since selfs-calls are easy to implement they are supported explicitly in
Clojure via the recur keyword, they won't blow up the stack, but tails calls
to other functions are not posible(without using some weird trickery) in
Clojure because of the limitations in the JVM.

------
kazinator
TXR Lisp: mlet (magic/mutual let), lnew (lazy new), lcons (lazy cons);

    
    
      1> (mlet ((z (+ x y))
                (y (succ x))
                (x 42))
           (list x y z))
     (42 43 85)
    
      2> (mlet ((z (+ x y))
                (y (succ x))
                (x (* z 2)))
           (list x y z))
      ** (expr-2:1) force: recursion forcing delayed form (* z 2) (expr-2:3)
      3> (flip *print-circle*)
      t
      4> (defstruct node () next prev)
      #<struct-type node>
      5> (mlet ((n (lnew node next n prev n))) n)
      #1=#S(node next #1# prev #1#)
      6> (mlet ((c (lcons 1 c))) c)
      #1=(1 . #1#)

------
kovrik
Reminded me of "Objects are merely a poor man's closures":
[http://wiki.c2.com/?ClosuresAndObjectsAreEquivalent](http://wiki.c2.com/?ClosuresAndObjectsAreEquivalent)

------
nhatbui
Why does get/set need to see increment? They don't call increment...

~~~
wickawic
The point he is trying to get at is that in an OO language you take it for
granted that all methods can call all other methods. However without such
trickery as described in the article this is not true in the simple language
with only functions.

~~~
tripzilch
It depends from what perspective you come at it, though.

The article makes the case that the "simpler language" is the one where a
function can only call another function if it has been declared before. Then,
the ability to call any function regardless of declaration would be seen as an
addition to that.

But couldn't one make an equally valid case that the "simpler language" is the
one where function declaration order is not significant, that unordered is
"simpler" than ordered? Then, the availability of a "F was declared before G"
relation would be seen as the addition.

After all, the only reason we intuitively feel there is an order to function
declarations is because the computer program's representation in source code
is linear text. But I'd argue that the more fundamental representation of a
computer program is that of a graph. Or at least equally fundamental :) It's
just that in practice, computer memory is linear, and so any representation of
a graph structure will have an implicit order that we need to tell the
computer to ignore. But mathematically this ordering is just a side-effect of
computer memory, irrelevant just like whether '1' bits weigh more than '0'
bits or not.

------
a_t48
This is super close to how you do objects in Lua, ignoring the fact that
metatables make for nice syntactic sugar.

------
dandare
I need that Pierce book 20 years ago. As a self thought I often regret that I
did not have the luck to stumble upon some amazing book instead of learning
fragmented bits from blogs and tutorials.

