28

I want to be able to call an anonymous lambda from within itself using Ruby. Consider the following recursive block (returns a factorial). I know I can assign it to a variable, and that variable is within the scope of the lambda:

fac = lambda { |n| n == 1 ? 1 : n * fac.call(n - 1) }
fac.call(5)

But, I want to be able to do the following (for no practical reason as of yet, I'm just interested in exploring the language some more):

(lambda { |n| n == 1 ? 1 : n * self.call(n - 1) }).call(5)

I know that won't work, because self is the main object. Am I doing it wrong? Am I trying to do something that's not possible -- and if not, is this because of some theoretical restriction or is it simply not implemented in Ruby?

lucapette
  • 20,564
  • 6
  • 65
  • 59
Edd Morgan
  • 2,873
  • 17
  • 22
  • 5
    Are you familiar with Y combinator? It might be not the best practical solution, but from theoretical point of view it's very interesting. If you don't, have a look at [this article](http://nex-3.com/posts/43-fun-with-the-y-combinator-in-ruby). Be careful, it might blow your brain out. – KL-7 Mar 01 '12 at 12:56

4 Answers4

13

In the following example, the lambda is still anonymous, but it has a reference. (Does that pass for anonymous?)

(l = lambda { l.call }).call

(Thanks to Niklas B. for pointing out the error in my original answer; I had only tested it in IRB and it worked there).

This of course ends in a SystemStackError: stack level too deep error, but it demonstrates the purpose.

mydoghasworms
  • 18,233
  • 11
  • 61
  • 95
6

It seems that anonymous function really doesn't have any reference. You can check it by callee

lambda{ __callee__ }.call #=> nil

And without reference you can't call this function. I can propose to you only a little more clean variant:

(fac = lambda{ |n| n==1 ? 1 : n*fac.call(n-1) }).call(5)
megas
  • 21,401
  • 12
  • 79
  • 130
  • 1
    It would *really* be a lot cleaner to just create a named function for that. – Niklas B. Mar 01 '12 at 14:03
  • Yeah, my original version did wrap the lambda in parentheses and I ran a .call() on there. This isn't really a question of code brevity, though, it's more about just how deep down the functional rabbit hole Ruby can go. KL-7 has a comment above linking to an article describing the Y combinator which is a very interesting read. – Edd Morgan Mar 01 '12 at 14:22
5
fact = -> (x){ x < 2 ? 1 : x*fact.(x-1)}

minimal function

Zoe
  • 27,060
  • 21
  • 118
  • 148
  • Almost. `fact = -> (x){ x < 2 ? 1 : x*fact[x-1]}` has one less non-whitespace character. – pjs Apr 22 '19 at 17:49
  • 3
    @pjs Most of the time parentheses are preferred over square brackets. Simply because square brackets mostly denote access to data (array, hash, struct, etc). Using parentheses clearly shows that you're calling a method/proc/lambda. Square bracket usage isn't wrong, but might mislead the unaware code reader. – 3limin4t0r Nov 13 '19 at 14:54
3

In addition to KL-7's comment, here's a Y combinator solution:

lambda { |f|
  lambda { |x| x.call(x) }.call(
  lambda { |x| f.call( lambda { |v| x.call(x).call(v) } ) } )
}.call(
  lambda { |f|
    lambda { |n| n == 0 ? 1 : n * f.call(n - 1) }
  }
).call(5) #=> 120

You would normally split these:

y = lambda { |f|
  lambda { |x| x.call(x) }.call(
  lambda { |x| f.call( lambda { |v| x.call(x).call(v) } ) } )
}

fac = y.call(
  lambda { |f| lambda { |n| n == 0 ? 1 : n * f.call(n - 1) } }
)

fac.call(5) #=> 120

Note that although fac is being assigned, it is not used within the lambda.

I'd use Ruby's -> syntax and .() instead of .call():

y = ->(f) {
  ->(x) { x.(x) }.(
  ->(x) { f.(->(v) { x.(x).(v) }) } )
}

fac = y.(->(f) {
  ->(n) { n == 0 ? 1 : n * f.(n - 1) }
})

fac.(5) #=> 120

The y invocation can be simplified a bit by using curry:

y = ->(f) {
  ->(x) { x.(x) }.(
  ->(x) { f.curry.(->(v) { x.(x).(v) }) } )
}

fac = y.(
  ->(f, n) { n == 0 ? 1 : n * f.(n - 1) }
)

fac.(5) #=> 120
Community
  • 1
  • 1
Stefan
  • 109,145
  • 14
  • 143
  • 218