views:

115

answers:

4

Imagine a simple (made up) language where functions look like:

function f(a, b) = c + 42
    where c = a * b

(Say it's a subset of Lisp that includes 'defun' and 'let'.)

Also imagine that it includes immutable objects that look like:

struct s(a, b, c = a * b)

Again analogizing to Lisp (this time a superset), say a struct definition like that would generate functions for:

make-s(a, b)
s-a(s)
s-b(s)
s-c(s)

Now, given the simple set up, it seems clear that there is a lot of similarity between what happens behind the scenes when you either call 'f' or 'make-s'. Once 'a' and 'b' are supplied at call/instantiate time, there is enough information to compute 'c'.

You could think of instantiating a struct as being like a calling a function, and then storing the resulting symbolic environment for later use when the generated accessor functions are called. Or you could think of a evaluting a function as being like creating a hidden struct and then using it as the symbolic environment with which to evaluate the final result expression.

Is my toy model so oversimplified that it's useless? Or is it actually a helpful way to think about how real languages work? Are there any real languages/implementations that someone without a CS background but with an interest in programming languages (i.e. me) should learn more about in order to explore this concept?

Thanks.

EDIT: Thanks for the answers so far. To elaborate a little, I guess what I'm wondering is if there are any real languages where it's the case that people learning the language are told e.g. "you should think of objects as being essentially closures". Or if there are any real language implementations where it's the case that instantiating an object and calling a function actually share some common (non-trivial, i.e. not just library calls) code or data structures.

Does the analogy I'm making, which I know others have made before, go any deeper than mere analogy in any real situations?

+1  A: 

There is a relationship between objects and closures. http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html

The following creates what some might call a function, and others might call an object:
Taken from SICP ( http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-21.html )

(define (make-account balance)
  (define (withdraw amount)
    (if (>= balance amount)
        (begin (set! balance (- balance amount))
               balance)
        "Insufficient funds"))
  (define (deposit amount)
    (set! balance (+ balance amount))
    balance)
  (define (dispatch m)
    (cond ((eq? m 'withdraw) withdraw)
          ((eq? m 'deposit) deposit)
          (else (error "Unknown request -- MAKE-ACCOUNT"
                       m))))
  dispatch)
z5h
Thanks for the links. I know there is an analogy between objects and closures. See my edit to my question, but I'm wondering if it ever goes any deeper than just an analogy, at least for the case of the relationship between instantiating an immutable object and calling a function.
jtolle
+1  A: 

Is my toy model so oversimplified that it's useless?

Essentially, yes. Your simplified model basically boils down to saying that each of these operations involves performing a computation and putting the result somewhere. But that is so general, it covers anything that a computer does. If you didn't perform a computation, you wouldn't be doing anything useful. If you didn't put the result somewhere, you would have done work for nothing as you have no way to get the result. So anything useful you do with a computer, from adding two registers together, to fetching a web page, could be modeled as performing a computation and putting the result somewhere that it can be accessed later.

Brian Campbell
Both this answer and z5h's are helpful to me, but see my edited question and the comment I left on z5h's answer. Clearly the analogy as you've described it is useless. But I was trying to constrain it...to instantiation of immutable objects and calling of functions. Does that make a difference?
jtolle
+2  A: 

You can't get much purer than lambda calculus: http://en.wikipedia.org/wiki/Lambda_calculus. Lambda calculus is in fact so pure, it only has functions!

A standard way of implementing a pair in lambda calculus is like so:

pair = fn a: fn b: fn x: x a b
first = fn a: fn b: a
second = fn a: fn b: b

So pair a b, what you might call a "struct", is actually a function (fn x: x a b). But it's a special type of function called a closure. A closure is essentially a function (fn x: x a b) plus values for all of the "free" variables (in this case, a and b).

So yes, instantiating a "struct" is like calling a function, but more importantly, the actual "struct" itself is like a special type of function (a closure).

If you think about how you would implement a lambda calculus interpreter, you can see the symmetry from the other side: you could implement a closure as an expression plus a struct containing the values of all the free variables.

Sorry if this is all obvious and you just wanted some real world example...

dave
This is very helpful. The real meta-question for me is to learn to think of different languages not as bags of independent syntactical elements that can be combined to do stuff, but as related (albeit with specific syntaxes) "wrappers" for more fundamental concepts. Thanks!
jtolle
@jtolle: You might be interested in the book *Concepts, Techniques and Models of Computer Programming* by Peter van Roy and Seif Haridi. They argue that programming languages can be decomposed according to the *Paradigms* they support (for example OOP or Logic Programming), and those paradigms can be further decomposed into *Concepts*. Peter van Roy has this great poster with the *Classification of the Principal Programming Paradigms*, which lists 34 paradigms, which are made up of about 18 or so concepts. So, those "fundamental concepts" you are looking for ... they even *call* them that!
Jörg W Mittag
@Jorg W Mittag, thanks for that recommendation! Book page here: http://www.info.ucl.ac.be/~pvr/book.html, link to freely available draft posted by author here: http://lambda-the-ultimate.org/node/3108#comment-45392, Poster here: http://www.info.ucl.ac.be/~pvr/paradigms.html, accessible looking ("programming paradigms for dummies") article here: http://www.info.ucl.ac.be/~pvr/VanRoyChapter.pdf
jtolle
Thanks again for this answer. I think it puts some flesh on the question I would have asked if I knew more. My question would have been clearer if I had given your lambda calculus example instead of my made-up "struct" example. But I'm satisfied that the relationship is only a conceptual one in "real" systems.
jtolle
+1  A: 

Both f and make-s are functions, but the resemblance doesn't go much further. Applying f calls the function and executes its code; applying make-s creates a structure.

In most language implementations and modelizations, make-s is a different kind of object from f: f is a closure, whereas make-s is a constructor (in the functional languages and logic meaning, which is close to the object oriented languages meaning).

If you like to think in an object-oriented way, both f and make-s have an apply method, but they have completely different implementations of this method.

If you like to think in terms of the underlying logic, f and make-s have a type build on the samme type constructor (the function type constructor), but they are constructed in different ways and have different destruction rules (function application vs. constructor application).

If you'd like to understand that last paragraph, I recommend Types and Programming Languages by Benjamin C. Pierce. Structures are discussed in §11.8.

Gilles
This is also a very helpful answer.
jtolle
Although I've accepted this answer because I think it most directly gets at what I was wondering about, I'd like to highlight the answer from @dave below. Between the two, I think it's safe to say that the answer is "no languages you'd develop in are implemeneted this way, but something sufficiently pared-down (like a lambda calculus interpreter) could well be".
jtolle