Hacker News new | past | comments | ask | show | jobs | submit login
Closures and Objects Are Equivalent (c2.com)
59 points by shawndumas on April 12, 2015 | hide | past | favorite | 45 comments



Just reading the submission title instantly reminded me of "Anton van Straaten's Koan", and for good reason -- it's quoted in the submitted page, and indeed makes up the first 3 paragraphs.

Here it is:

  The venerable master Qc Na was walking with his student, Anton. Hoping
  to prompt the master into a discussion, Anton said "Master, I have
  heard that objects are a very good thing - is this true?" Qc Na looked
  pityingly at his student and replied, "Foolish pupil - objects are
  merely a poor man's closures."

  Chastised, Anton took his leave from his master and returned to his
  cell, intent on studying closures. He carefully read the entire
  "Lambda: The Ultimate..." series of papers and its cousins, and
  implemented a small Scheme interpreter with a closure-based object
  system. He learned much, and looked forward to informing his master of
  his progress.

  On his next walk with Qc Na, Anton attempted to impress his master by
  saying "Master, I have diligently studied the matter, and now
  understand that objects are truly a poor man's closures." Qc Na
  responded by hitting Anton with his stick, saying "When will you
  learn? Closures are a poor man's object." At that moment, Anton became
  enlightened.
Now I will be the first to admit that I usually just don't get koans. But I think I get this one. Objects and closures are equivalent in the sense that what you can do with one you can do with the other. However, for each particular case one may be better (easier to use, more natural, whatever) than the other. You should use which ever one is the best for each particular problem.

This is of course easier if you are using a programming language that natively supports both closures and objects...


The way I like to put it is that instances and closures are duals: a closure can do exactly one thing, while an instance can, in general, do several things. That's why, when invoking an instance, you have to supply a method name that says which of the several things you want it to do, while when invoking a closure no method name is necessary.

Of course it's not a hard-and-fast distinction: you can have a closure that dispatches on one of its parameters to do one of several different things, and you can have an instance with only one method. But it's usually valid.


If you think of a closure as only returning a reference to a function, but a closure can return anything. What if my closure returns an ssh connection to a new machine? A database? A program that generates programs? A hash of functions specialized to my calling arguments? How is a the function that returns a closure different from a constructor?


You can have several different functions that all close over the same state. This situation is analogous to an object with several different methods.


You can also do it over just one function that takes in a variable that modifies behavior.

Imagine you have a closure with a function that takes in a string and a list and returns a list. Depending on implementation of the function, you could have the string be the operation you wish to perform. The method name, and the list be the parameters. The returned list is the output.

Getters could pass an empty list and return a list with one entry

Setters could pass a list with a value and return an empty list

And so on :)

Like this:

   func NewClosure() func(string, ...float64) []float64 {
      x := float64(0)
      return func(method string, args ...float64) []float64 {
         if method == "setX" && len(args) > 0 {
            x = args[0]
            return nil
         } else if (method == "getX") {
            return []float64 { x }
         } else {
            panic("invalid method")
         }
      }
   }

   a := NewClosure()
   b := NewClosure()
	
   a("setX", 50)
   b("setX", 12)
	
   fmt.Printf("a.X = %v, b.X = %v", a("getX")[0], b("getX")[0])
Runnable example here: http://play.golang.org/p/68NTyEx6_P

I like it.

[edit: added working example .. and realized I'm adding to what my parent poster was saying]


For this reason, I think of objects as not equivalent to closures, but rather ML-style functors. I think implementing objects as records with closures as fields is really just a poor man's parameterized module.


I think it depends a lot on the language's syntax and what sugar it offers. I believe there are choices you can make that make closures superior to objects in every possible way.

Consider this simple syntactic rule: `obj.x` <=> `obj("x")`. With this rule, the usual syntax for a method call, `obj.method(x)`, literally becomes the curried application of a closure, `obj("method")(x)`. You could also add a rule like `obj(x) = y` <=> `obj(set(x, y))`. Then you could define objects a bit like this:

    point = (x, y) -> method ->
        match method:
            "x" -> x
            "y" -> y
            set("x", newx) -> x = newx
            set("y", newy) -> y = newy
            "add" -> (pt) -> point(x + pt.x, y + pt.y)
    p = point(1, 2)
    p.x = 5
    print p.add(point(3, 4))
All closures. And the nice thing with that scheme is that meta-programming and reflection become completely trivial. Control over the interface is total. What use is there for "real" objects in this situation?


Your approach does not support polymorphism.


What sort of polymorphism did you have in mind?


Subtyping polymorphism


They are not exactly equivalent though. It is not possible to completely abstract away state changes with objects. You always end up with at least two calls that you have to keep track of.


I'm not sure that I would agree here. Do you have a simple example?


  foo.DoSomething()
  ... // remember not to return 
      // without foo.Done() called
      // (EDITed for clarity: 
      // foo is the state, that changes,
      // so you have to keep track of 
      // its changes)
  foo.Done()
vs

  DoSomething (func(){
     ... // don't have to remember 
         // a thing, no side-effects, 
         // equivalent of .Done() is
         // called afterwards for you
  })
EDIT2: exact behavior is not the point, keeping track of things is.


That doesn't seem to have anything to do with objects versus closures. In both cases the right answers is absolutely to pass a function to DoSomething be it a function or a method.

If you're considering a language deficient of functions then we can take advantage exactly of the idea that objects can embed functions by having a "callable" thing.

    foo#doSomething (object
      method call : unit -> unit =
        ...
    end)


Ok, you are right. I was confusing implementation with the concept.


Classes don't need to have mutable state. I've written several rather useful classes that were completely immutable after initialization. As such, there are no things to keep track of. No side-effects, nothing like that, a snapshot in time of whatever values I need to pass elsewhere.


Pass an object to DoSomething and you get the same thing as the closure.


Java's Runnable interface was an example of this in practice. Rather than passing a function, you passed an object whose primary purpose was to carry around a function that would be called.

   DoSomething(func() { ... })
becomes

   DoSomething(new Runnable() {
      public void run() { ... }
   });
or the longer form

   class MyAction implements Runnable {
      public void run() { ... }
   }

   DoSomething(new MyAction());


Closures are an implementation technique and objects a semantics. To equate them is to confuse implementation and interface.

(Functions + tuples + recursion) is powerful enough to embed objects very nicely though!


You need a term to distinguish non closing over closing functions, hence the closure leakage. Or maybe HOF ?


Functions that don't "close over the environment" are ones that are defined in the null environment. Normally you have

     Gamma, x : a  |-  e : b
    --------------------------
      Gamma  |-  \x -> e : b
But you could also have "bare functions" if you liked

       x : a  |-  e : b
    ----------------------
      .  |-  \x -> e : b
This would force all names to be explicitly applied, I think.


The use of 'closure' is an idiomatic shortcut to express that.


Sort of. It's also a didactic method to talk about it and it often is quickly followed with people talking about stack pointers or environment frames. You can blur the lines if you like, but I find it tends to pay to be precise here.


hi tel, would you mind elaborating on what you mean by "objects [are] a semantics"?


A closure is a data structure commonly used to represent a first-class function (containing a function pointer for the code and some sort of structure mapping the environment's bound variables to their values). Implementing a language with first-class functions does not require using closures. You could have an interpreter that performs beta reduction on the syntax tree, and first-class functions would still behave the same way. So closures are an implementation detail.

In OOP, there are pieces of data that have operations associated with them. Two objects are free to have their own "version" of the same operation (same-named), and when you invoke it on an object, you get that object's version. In writing one of those operations, you have a handle on the thing you're operating on without having to take it as an argument. This specifies what result you'll compute and what side effects you'll cause by invoking an operation (i.e., the semantics). It does not specify things like where in program memory the code for that operation will live or how invoking it will work (implementation).


I like to see this as a sort of duality: closures are objects that have a single method (call) and objects are made of functions that can only capture one variable (this).


You can have as many methods as you like. Here's an example comparing one with the other in Golang: http://play.golang.org/p/TXZlZL7Tl8


That's not a closure. That's returning a struct. That struct is an object that has several instance members that are functions, which you've created as closures, but note you're still returning a struct. To return a closure, you need to return a function type, which then has exactly one way to call it, which is the claim here about functions.

Of course you can dispatch anything you like within that call, turning a closure back into "methods", although in most languages that's a fairly painful way to operate even though if it is possible. Personally over the years I've come more around to tel's point of view, which is that as related as objects and closures may be, they aren't quite just two sides of the same coin, as further evidenced by the fact that pretty much all modern OO languages also include closures. If they really were the same thing in two guises somebody would probably have fully unified them by now, but they aren't the same thing. As much fun as the koan is, I think it's false wisdom.


> which you've created as closures, but note you're still returning a struct

a struct of three closures :)

EDIT:

I'm not sure I understand your point. Here's how to return three closures without the struct.

http://play.golang.org/p/9UtJ7ZBMzb


Yes, I said that. Please try less hard for the "gotchas". There's no ambiguity here, the Go type system does not permit it; something is either a struct or a closure (or exactly one of the other valid types; even "interfaces" are actually a specific type in Go), and the linked code returns a struct. (Python, for instance, can create something that acts as both object and closure at the same time with an object that implements the __call__ magic method.)


The spot I start to feel fuzzy, though, is if you start talking about higher-level abstractions. E.g, are interfaces equivalent to type classes?


You have to specify the language; there's no (useful) definition of type classes and interfaces outside of the context of a specific language that is specific enough to make reliable comparisons.

If you mean "Are Haskell type classes equivalent to Go interfaces?", the answer is no, Haskell type classes are substantially more powerful, even before you start turning on extensions. For instance, "Go" can not express the Monad typeclass at all.


I was more thinking Haskell type classes vs. C# or Java interfaces. They're a bit more powerful than Go interfaces because they can be used in combination with generics.


The Monad type class is a pretty good test. Last I knew it wasn't properly expressible in C# or Java either in the fully general sense, though you can get closer than Go, certainly. Whether it gets close enough that you can call it "done" is a bit of a judgment call; in the end, the semantics are never quite identical even in the best of cases.


As others have exemplified: it gets really hard to talk about this stuff outside of formal models. Especially if your goal is to talk about equivalency.

At this point I start trying to turn toward System F, but that's my hammer for this particular nail and I don't know an equivalent one in the object side of things. I'm certain you can express higher-level things in System F which are equivalent to interfaces. I know there's a translation of typeclasses to System F—it's called Haskell, har har—although you have to recognize that the "search" component of typeclasses will be lost.


Interfaces—at least in C#/Java—are equivalent to product types; they let you express the notion of conjunction within the type system.

Similarly, “implementation inheritance” (where every class is either sealed/final or abstract) is how we express sum types.

We can simulate type classes in C# by using implicit casts to abstract classes (interestingly, it’s not so straightforward to simulate the awesomeness of type classes in F#).

The key to all of this is to realize that “types” and “classes” are not equivalent, even though C# and Java conflate them.


Yes, it's not very formal and as others said it would need some definitions to make a useful result.

But actually I don't think that this duality is related to types: in a static language, the types of captured variables does not appear in a value's type. And for the object side, the type of instance variables is hidden behind the public interface too.

So it's really more about techniques for creating abstractions than how the values are composed together.


Depends if you have multiple inheritance.


A closure is 1 function and an environment. An object is N functions and an environment.

Objects are more useful as you don't need to repeat the environment to implement more than 1 operation.

Both objects and closures can be type erased so that the environment is not part of their type.


That single closure function could return e.g. a map containing more functions that share the same environment.


Conceptually they're both just a combination of some data and code that (usually) makes use of that data. I think a great example of this is function objects in C++.


Closures are used as objects quite a lot in R, as the standard structures are all actually immutable (to first order the only common user facing mutable things in R are variable bindings / environments, there are reference classes like RC and R6 but they are not considered core language). A lot of R users may not know this as value mutability is simulated through aggressive variable re-binding.


See Kiselyov fp-oo relation ML thread

http://okmij.org/ftp/Scheme/oop-in-fp.txt


Wouldn't closure be more like a single-method-objectin a class that has no class methods?

A clojure carries its own states (variables) that it, only, can modify. While the states (variables) of an object can be changed by another method for the same object, or by a class method, or even methods from another object if it uses method variables.


... but this would eliminate huge amounts of internet discussions. We better leave it as it is.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: