Live data from Hacker News

Generalizing Support for Functional OOP in R

blog.r-project.org

41–50 of 53 posts

Re: Generalizing Support for Functional OOP in R

#41
post #4

Earlier quoted context omitted.

OOP has been a critical part of real-life R for a long time, especially in complex implementations of classes of kernels, algorithms, and so on with S4, and more general-purpose with R6. Without these frameworks it would be difficult to implement them. Personally I find it more expressive for general-purpose computation than Python. The "fs" library is much better at working with files and paths than Python "os" and…

> I suspect lazy evaluation is a part of this. I had no idea R was lazy. Makes me wanna learn it now.

R is such a weird little language. It's basically lazy Lisp dressed up in C syntax.

For example, the only operator it really has is a function call. Everything else is syntactic sugar for a function call, and I mean literally everything: assignments, conditionals, loops, even function definitions and curly braces are all function calls. For example:

   a 
and so on. You can actually see what things look like under the hood for any R expression or statement by printing as.list(quote(...)), and recursively doing that for every element in the resulting list

The reason why this is possible is because all arguments in R are not evaluated when passed to a function. Instead, it receives the expression object corresponding to the expression that the caller used for that argument, combined with the environment in which it was created - R calls this a promise. It's kind of like instead of (foo (+ x y)), you'd write:

   (foo (`(+ x y) (lambda () (+ x y)))
i.e. for the argument, instead of its evaluated value, you passed both the quoted expression and the lambda that computes it in the original environment. When the actual value of the argument is needed, the expression is evaluated and the result is cached in the promise (so implicit eval is lazy and one-off). But the function can instead just query for the argument expression directly and then use it in some other way - so e.g. the `<-` function does not eval its first argument, but instead uses it to identify the variable being set.

Re: Generalizing Support for Functional OOP in R

#42
post #4

Earlier quoted context omitted.

OOP has been a critical part of real-life R for a long time, especially in complex implementations of classes of kernels, algorithms, and so on with S4, and more general-purpose with R6. Without these frameworks it would be difficult to implement them. Personally I find it more expressive for general-purpose computation than Python. The "fs" library is much better at working with files and paths than Python "os" and…

Pathlib gives a decent oop interface to most file operations. The pathlib docs has a handy Rosetta Stone table for replacing `os` and `shutil` invocations which is very handy. It could still be better, and sadly being in the stdlib means it probably won't be improved. For example, we still need shutil.rmtree to recursively nuke a directory. The pathlib way of doing it is laborious and error prone: https://stackoverfl…

Pathlib is a great addition and it's nice that it's in the standard library. I like fsspec also, which has some functionality that overlaps with Python's existing libraries but makes it a little cleaner IMO.

Re: Generalizing Support for Functional OOP in R

#43
post #10

This seems like a huge mess. Why is it so hard for R people to settle on a standard?

There's a few reasons I can think of.

First, R definitely needs at least two, one for functional OOP (Common Lisp style) and one for class-based OOP (Java style). The latter is _much_ less important for everyday R users but as a package author it's extremely helpful for modeling certain types of resources. (Interestingly, Python also ships with two: @singledispatch and classes; and multimethod/multidispatch also exist.)

Second, because R's basic language building blocks are so flexible, it's relatively easy to build new OOP systems, resulting in more diversity.

Third, I believe it's actually been close to thirty years since S4 was introduced, which was the last functional OOP system until S7. I don't think that's a terrible track record, compared to how much variety you see in equally fundamental systems in other language communities (just off the top of my head, Python: packaging standards, environment management, data frames; JavaScript: module systems, runtimes, package managers).

Re: Generalizing Support for Functional OOP in R

#44
post #24
post #20

Nice. This is a bit disappointing though: “Multiple dispatch is heavily used in S4; we don’t expect it to be heavily used in S7, but it is occasionally useful.”

Why is that disappointing?

Maybe disappointing is not a good way to describe it but I couldn’t find a better word. I meant that it seems that multiple dispatch won’t be highlighted.

I would have liked to see a better S4 - fixing some of its issues and adding things like before/after/around method - and I’m not sure this goes in that direction. It can still be an improvement in practice over the rarely-used S4 though.

Re: Generalizing Support for Functional OOP in R

#45
post #26

Earlier quoted context omitted.

Maybe too expressive and too flexible? Different R programs can have wildly different dialects, making it difficult for two R programmers to even understand each other. I’ve seen comics depicting the learning curve for R as having local minima beyond which there are further peaks and troughs of knowledge. A beginner might learn enough to get by, but find the code of someone on the other side of one of those peaks to…

This is fair. For what it's worth, Python is tending toward this and I think is introducing newer syntax at a faster rate with things such as structural pattern matching and typing, which I have had difficulty explaining to people who don't keep up with each new release.

Switching from R to Python, this resonates. I write base (non-tidy) R, and it's definitely another language from tidy. That said, having written a fair amount of base Python, jumping into torch/tensor flow feels like an even further separation than base R/tidy.

Re: Generalizing Support for Functional OOP in R

#46
post #4

Earlier quoted context omitted.

OOP has been a critical part of real-life R for a long time, especially in complex implementations of classes of kernels, algorithms, and so on with S4, and more general-purpose with R6. Without these frameworks it would be difficult to implement them. Personally I find it more expressive for general-purpose computation than Python. The "fs" library is much better at working with files and paths than Python "os" and…

I too much prefer R to Python (it’s far more expressive, for one) however it’s clear now that Python has “won” in this space and R is a tough sell to a wider team.

My experience is that you can "sell" R when the statistical or modelling technique is not (or not well) implemented in python. Which still includes a lot of potentially useful statistical techniques! R should/could lean into being the tool that python programmers reach for when they need something a little less mainstream, if they made it easier for non-R programmers to do so.

Re: Generalizing Support for Functional OOP in R

#47
post #6

To be honest, OOP never really seemed like a good fit for R. Functional programming is a much more natural fit, given that both it and R come from a mathematical point of view. R is a great language for the mathematical/statistical stuff it was invented to do, but I don't think it will ever be a general-purpose language, and it probably would become worse at its core purpose if it tried to. More work on being easily…

OOP as used in R is very much a function of API design and not a function of routine R usage for data analysis. To many users of R they are not even aware that they are using OOP at all, especially for the S3 style of objects. When you have an object, like `model summary()` ends up being very natural, and when you fit in the tidyverse operators many very complex workflows end up being very easy to digest. But when yo…

Interesting! To prove your point, I was unaware that I was using OOP in R when I did those kinds of things. But, it feels more like functional programming, and I wonder if "OOP" is even a good way of describing that, if that's what they mean?

Re: Generalizing Support for Functional OOP in R

#48

Earlier quoted context omitted.

Thank you for the correction. Is it possible to use NSE in say Python or JavaScript?

Yes, though the languages do not support it explicitly you can simulate lazy evaluation by wrapping all your arguments in closures. This way they won't be evaluated until called within the function body.

Sidenote, the evaluation model of python can be surprising. List comprehension will create implicit function scopes that can trip you up.

Re: Generalizing Support for Functional OOP in R

#49
post #41

Earlier quoted context omitted.

> I suspect lazy evaluation is a part of this. I had no idea R was lazy. Makes me wanna learn it now.

R is such a weird little language. It's basically lazy Lisp dressed up in C syntax. For example, the only operator it really has is a function call. Everything else is syntactic sugar for a function call, and I mean literally everything : assignments, conditionals, loops, even function definitions and curly braces are all function calls. For example: a and so on. You can actually see what things look like under the h…

Thanks for a great explanation. It looks like the thing Scala does with its by-name parameters, but for every parameter by default. Even closer analogy, I think Io works in a very similar way - bodies of methods can access their arguments as Message (ie. unevaluated calls) objects and then decide to evaluate them as needed (which differs from your example in that the body can choose the context in which the message send is to be executed, it doesn't have to be lexical scope of a caller). It enables a great deal of expressivity - esp. coupled with some syntactic sugar for "operators" - and I always wondered why more languages don't have that feature.

Re: Generalizing Support for Functional OOP in R

#50
post #41

Earlier quoted context omitted.

R is such a weird little language. It's basically lazy Lisp dressed up in C syntax. For example, the only operator it really has is a function call. Everything else is syntactic sugar for a function call, and I mean literally everything : assignments, conditionals, loops, even function definitions and curly braces are all function calls. For example: a and so on. You can actually see what things look like under the h…

Thanks for a great explanation. It looks like the thing Scala does with its by-name parameters, but for every parameter by default. Even closer analogy, I think Io works in a very similar way - bodies of methods can access their arguments as Message (ie. unevaluated calls) objects and then decide to evaluate them as needed (which differs from your example in that the body can choose the context in which the message s…

Oh, I loved using Io in the late 2000s (and did all my algorithms assignments in it, probably to the chagrin of the instructor who allowed us to use any language we wanted). Maybe that explains some of my affinity toward R. Is there anything else useful that is similar to it these days?
Post reply on HN