Hacker News new | ask | show | jobs
by dialamac 1954 days ago
> the language designs are so similar that I could just as well imagine a world where Python is the web development lingua franca, and Ruby has all the machine learning libraries.

Well aside from the startling implication that Ruby is a web development “lingua Franca.”...the latter statement is reasonable, as it turns out language design isn’t actually that important here. But the former is pretty far off the mark. I mean, Ruby doesn’t even have first class functions and is very strongly smalltalkish in its OO purism, it has mutable strings a-la Perl. The async story is obviously quite different. Python has a much more complicated interpreter, which has contributed to it being more difficult to get even simple optimizations that are done in Ruby. They’re really only similar in the most superficial sense... in the same way that all current dynamic interpreted languages will do certain things similarly.

5 comments

While I agree with most of your comment, "Ruby doesn’t even have first class functions" sticks out. Not because it's not true, but because it doesn't really matter as Procs are first class in Ruby and can be used in all the same ways, so it doesn't really affect things except introducing something you might need to learn coming from JS or any other language where functions are first class.
>it doesn't really matter

This way can be argued for almost every language, as they almost all allow some form of passing a piece of code to a function. The difference is what matters - whether that's elegant enough to be widely used or not. Ruby is, frankly, somewhere in between.

> Ruby doesn’t even have first class functions

I'm genuinely curious what is it that you miss in Ruby regarding functions (my Ruby is pretty OOPish and I try to avoid functional magic).

AFAIK you can do stuff like currying/partial application with lambdas and you can get a hold of any method by name and use it as a lambda, pass it to other functions, use it as a block etc. Is there something we're missing compared to, say, JavaScript?

You can pass a function as argument using its name in Python and then call it. You must pass it as a symbol in Ruby and then send to it.

  def f(x):
    return x

  def g(fn, x):
    return fn(x)

  g(f, 1)
Ruby can definitely pass around several varieties of closure and related constructs, including procs, blocks, lambdas, bindings, continuations, fibers, and both bound and unbound methods.

Whether we should is another matter, and the syntax and idioms certainly lean towards preferring a symbolic late binding, but the language is multi-paradigm, and one may write purely functional Ruby if desired, immutable values and all.

No, passing a symbol and sending is a different mechanism.

The send method is basically the same thing as calling a method by name, as in "obj.foo" == "obj.send(:foo)". If you only pass a symbol into your "caller" method, the symbol goes through the normal lookup: does the receiving object respond to this? If yes, then that implementation (at that exact moment) is called, if not, you get a method_missing.

You're right that that's not how you do first-class functions in ruby. Your example in ruby would be:

  def get_and_call(fn, x)
    fn.call(x)
  end

  def upcasinator_method(str)
    str.upcase
  end

  upcasinator_lambda = lambda {|str| str.upcase}

  get_and_call(upcasinator_lambda, 'foobar')
  => "FOOBAR"

  get_and_call(method(:upcasinator_method), 'foobar')
  => "FOOBAR"
As you can see there are two ways to do that -- either you create a Proc (a lambda if you care about arity), which is the first class function in Ruby, and you call that, or you define a method (but that's a method, an OOP concept, a procedure that implicitly operates on an object), you get a hold of it using the "method" method and then you call it.
The exact same thing in Ruby:

    f = ->(x) { x }

    g = ->(fn, x) { fn[x] }

    g[f,1]
or even¹

   h = g.curry(2)[f]
   h[1]
function composition is available:

   f = -> x { x * 2 }
   g = -> y { y + 4 }
   j = f >> g
   k = j << g

   j[1] #=> 6
   k[1] #=> 14
and, topically, the Y combinator:

    y = -> f {
        -> g { g[g] } [
        -> g { f[-> v { g[g][v] }] }
      ]
    }
hence

    fib = y[-> f {
      -> n { n < 2 ? Array(0..n) : f[n-1].then {  _1 << _1[-1] + _1[-2] } }
    }]

    fib[6] #=> [0, 1, 1, 2, 3, 5, 8]
although the more idiomatic Ruby might be

    fibo = Hash.new { |this, n| this[n] = n < 2 ? n : this[n-1] + this[n-2] }

    (0..6).map(&fibo) #=> [0, 1, 1, 2, 3, 5, 8]
in which the Hash self-converts to a closure via the & operator.

Anyone who says "Ruby doesn't have first-class functions" can fight me.

--------------------

⁽¹⁾ notwithstanding that I recall Matz once remarked the curry method is only included as an elaborate joke.

Thank you. I forgot the Proc#[] method.

I never thought I would ever write these words, but the Python equivalent is more beautiful. Ouch.

> You must pass it as a symbol in Ruby and then send to it.

This is just plain wrong. You can absolutely do this, as other commenters have pointed out.

It is common to see Ruby code passing around a symbol and sending it, but my guess as to why this pattern became common is that it can be serialized into plaintext like YAML, stored in a database, and called later, like for a background job in a web app. But there's no need to do it this way.

Another reason you don't tend to see it this way in Ruby is because Ruby optimizes for the common case: calling methods [1]. Because of this, you can easily create very concise DSLs in Ruby, which would never be quite as clean in Python.

1: https://yehudakatz.com/2010/02/21/ruby-is-not-a-callable-ori...

Wait, Ruby doesn't have first class functions?

I'm pretty sure it does and that it is full-fledged, compared to Python's intentionally restricted `lambda` syntax.

Nope! You can't pass functions, return them from other methods and so on, so it's not first class.

What you do have in Ruby is Proc, that can be used kind of like first class functions, so you don't really miss out on much.

Maybe I misunderstand your use of the word "function" here, but in addition to Proc/lambda you can certainly reference a method, pass that around, return it, call it?

See:

  * https://ruby-doc.org/core-2.7.1/Method.html
  * https://ruby-doc.org/core-2.7.1/Object.html#method-i-method

    $ irb
    2.3.3 :001 > p = Kernel.method(:puts)
     => #<Method: Kernel.puts> 
    2.3.3 :002 > p.call "Hello"
    Hello
     => nil 
    2.3.3 :003 > def run_a_method(meth, *args)
    2.3.3 :004?>   meth.call(*args)
    2.3.3 :005?> end
     => :run_a_method 
    2.3.3 :006 > run_a_method(p, "Hi!")
    Hi!
     => nil 
    2.3.3 :007 > 
ps. this also works with the "to_proc" operator (&) so you can do this:

    [1, 2, 3].each(&Kernel.method(:puts))
(but I wouldn't necessarily recommend this since it's not very readable, at least to my eyes).
Yeah, and that's not what first-class usually means. I simply can't say "my_method = Kernel.puts" the same way I can say "my_class = Kernel". Ruby authors seemed to want to make parentheses optional by any price, and that killed the opportunity to make methods first class.
You can literally write my_method = Kernel.method(:puts) and it’ll give you the method to do what you wish with.

As far as defining anonymous functions, literally all you have to do is:

my_func = -> (input) { output }

That makes a “lambda” which is literally just a function you can pass around anonymously.

You call it via either my_func.call(input) or simply my_func[input].

That capability is used all over the place.

Ruby absolutely has first class functions, the only thing you can say is it the syntax is slightly different if you want to use them anonymously (with no object context).

> my_method = Kernel.method(:puts)

That's exactly what being first-class is not. You tell Kernel to wrap you a method called puts into an object of class Method, rather than assigning a method to a variable. You don't have to look further than JS for a counterexample.

So the functionality is equivalent, but ruby's syntax is slightly longer. Is there something other than ruby's slightly longer syntax that you find lacking?

Ruby's Method class matches the design "everything is an object" (strings, numbers, modules & classes themselves...). Are regular expressions first class in ruby? There is top-level syntactic sugar for REs (/.../) but REs are just instances of `Regexp`. It's just syntax. I don't think you can argue Python REs are first class: I have to drag in `re` and then `re.compile()` a string (and my editor won't know to syntax highlight metachars or interpolation). I use REs more often than method references in either language...

But I'm not trying to "whataboutwhataboutwhatabout" here. I'm trying to say there's a difference of philosophy. Ruby Methods are just more objects (Method<Object). They aren't some sort of blessed type, just like everything else. Everything descends from Object. Kernel is just a module. Method is just a class. And so on. In that context, what does it mean to be "first class"? Objects are first class and methods are objects. QED. No?

(FWIW I agree on ruby's optional parens, too much inconsistency for a few less lit pixels on the screen).

> In that context, what does it mean to be "first class"

It's easy. First, what does it mean to be "first class"?

> a first-class citizen (also type, object, entity, or value) in a given programming language is an entity which supports all the operations generally available to other entities. These operations typically include being passed as an argument, returned from a function, modified, and assigned to a variable

Secondly, how does that apply to functions?

> [First class functions] means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures

In what way doesn't Ruby have first class functions? This article explains it better: https://codequizzes.wordpress.com/2014/05/19/ruby-methods-ar...

In practice, because of Proc, you can use Proc wherever you'd use functions and get the same effect in practice, so this is not a big issue. But just because Ruby has Procs, doesn't mean it has first class functions, which is a specific concept.

Sorry, Ruby methods are not objects. They can surely be wrapped in objects of class Method, but this feature is used quite rarely. Whether this is due to the syntax, the confusion regarding the evaluation scope, or something else, I have no idea.
> and my editor won't know to syntax highlight metachars or interpolation

IntelliJ editors do this and it’s insanely useful. Works with SQL, regex and any other language.

This is incorrect. If you want an anonymous function (with no object context) you can write a lambda, store it to a value, make other lambdas that call it and return yet another lambda etc.

You can do the exact same thing with object methods if you want, eg. my_puts = Kernel.method(:puts).

You can even do this in one line and stop the auto parenthesis behavior if you want.

foo = method def foo ... end

Now foo will reference the method and you have to use foo() to call it.

The reason that people don’t do a bunch of that in Ruby is that the support for anonymous blocks is so convenient That there aren’t that many cases where you really need anonymous functions detached from any object.

Ah, so it's the same then
Not really the same since lambda in Python is just a syntactic variant that is restricted. A function defined using `def` can be passed around without restrictions, which you simply cannot do in Ruby.
What are the restrictions on passing around a ruby `Method` object?

(Please see my sibling comment for example).

There is no practical difference in Python functions and Ruby lambdas.
> it has mutable strings a-la Perl

Yes, but they're not popular these days. Ruby 3 almost defaulted to frozen-strings by default (I wish it had) and a lot of the ecosystem moved to that style resulting in some nice memory saving. Rails enforces it on its code for example: https://github.com/rails/rails/blob/0f09dfca363410f51f6f6078...

You are talking about string _literals_, there is no motion nor interest I'm aware of in making all strings immutable in Ruby, thankfully.
new crew dialamac gotta be dissin / says Ruby first class functions are missin / tries it on in da y combinator / but

    y = -> f {
        -> g { g[g] } [
        -> g { f[-> v { g[g][v] }] }
      ]
    }
/ see ya later.
I’m amused that a number in this thread saw these observations as a “diss”... that’s more on you then me. I didn’t pass any value judgement on these differences, merely that they exist and demonstrate some fundamental differences in the history of these languages. While Ruby lambdas are essentially first class anonymous functions they were added late in the language and they are distinct from methods that predate them. You can’t just drop them in seamlessly where a method is used. The point stands that while both Ruby and python have accreted more stuff as time wears on, their initial design principles were starkly dissimilar.
Yo bro dialamac / Caught in a falsehood / Tryin' to dial it back / Feels misunderstood / Sez lambda came late / But changelog don't lie / Since v0.8 / Ruby so fly.

Or, in prose form: I don't see anyone disagreeing that Ruby & Python are dissimilar both in principle and in practice, but "Ruby doesn't even have first-class functions" was most unreservedly an epic howler, and once played, folks were inevitably gonna have some fun passing that football around, and despite most of the changelog from 1995 being in Japanese there are nevertheless references to lambdas that early on, although the more concise "stabby" syntax didn't rear up until ca.2008.

[See screenshot]
> "Functions in Ruby are methods"

RECORD SCRATCH. THE ROOM FALLS SILENT.

NARRATOR: A common assumption, but no. Equating object methods to functions is a furphy. The argument along the lines of:

"Ruby's object methods are Ruby's functions, but you can't pass them around, ergo they're not functions"

is using the term "function" in two different ways, but assuming they're the same; this is not an argument based on substance, but upon mislabelling. The conclusion is bogus because the premise is bogus.

It may arise from a category error, assuming that the thing depends intrinsically upon the literal representation of the thing, or (worse) the common name of the thing, but this is a) wrong anyway, and b) loses coherence entirely in a language in which function literals can be conjured and lexically rebound at runtime.

In actuality, Ruby's lambdas are functions, and first-class, by the only definition with substance: they are closures capable of higher-order expression, taking functions as parameters when invoked, and returning functions as results.

Which is why saying "it don't have them" on a forum named after a fixed-point combinator is to invite: a) ridicule, and b) lambda calculus expressions in rap battle form.

CROWD: Yeah!

MUSIC STARTS / GLITTERBALL CLOSEUP

I’m glad you think highly of your poetic abilities, but I have no desire to engage in that pointless argument.

Edit: and yes.. uh comment deleted as charged, since you had edited after I replied.