1. 43
  1. 39
      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.
    
    --- Anton van Straaten
    
    1. 6

      Lo, I came here to post that very koan, and saw upon my arrival that it had already been done.

      1. 14

        And now you are enlightened.

      2. 2

        This is especially true if you know how objects in Smalltalk / Self work.

      3. 17

        You can go even further and remove the need for if statements and booleans:

        function cons(a, b) {
            return fn => fn(a, b);
        }
        
        function getFirst(pair) {
            return pair((a, b) => a);
        }
        
        function getSecond(pair) {
            return pair((a, b) => b);
        }
        
        1. 3

          Beautiful. Truly data out of nothing but procedures.

            1. 4

              I’d encourage you to implement getFirst as written here, with two arguments (a, b) for the lambda passed to pair. I know it’s safe to omit the extra parameters in JavaScript, but it made me hesitate for a few seconds while mentally parsing the code.

              1. 2

                Good call. Done

            2. 2

              I like this approach better because it’s more “code-y” than the second approach mentioned in the post.

            3. 9

              Is there a reason you’re doing SICP in javascript? Isn’t half the fun getting to use Scheme?

              1. 4

                That’s probably true. But it’s also a lot of fun doing it in a language I know well. Plus it still requires reading a lot of scheme.

              2. 8

                I got pretty deep into chapter 3 last year. The realization in the article really hit me when watching the corresponding lectures from 1986. Just as amazing as the book.

                Also, SICP is a surprisingly great introduction to creating abstractions, which was not what I was expecting. Pairing it with A Philosophy of Software Design (which contrasts nicely to SICP’s practicality) I think would be great reading for any developer.

                1. 7

                  I think there’s an inadvertant misleading statement early on: cdr does not get the second item in a list, as the article plainly says.

                  It plays a role in so doing: cdr gets the “tail” of the list, i.e., what remains past the head.

                  I’m too young to have experienced it first-keyboard, but i think “car” and “cdr” were assembly language instructions for “contents, address register” and maybe “contents, data register”, IIRC, alluding to an early Lisp implementation’s underlying assembly language.

                  Back to the article: cadr would get the second item in a list, if it existed, or (car (cdr someList)) would be what the author was thinking, it seems.

                  1. 7

                    The article says that cdr gets the second item in a “pair”, which is correct. For example, if we run:

                    (cdr (cons 1 2))
                    

                    We get 2. Which is the second item in the pair. If we use cons cells to build up a list, you’re correct. Running:

                    (cdr (cons 1 (cons 2 '())))
                    

                    Gets us back the “tail” of the list, which is really the second item in the first cons cell.

                    cadr gets the second item of a list because it gets the first item of the second item of a pair, where the second item is also a pair.

                    1. 6

                      Thank you: explained more clearly than I did.

                      1. 2

                        I actually meant that the article has it correct, although I didn’t explain it that well.

                        The article does not say that cdr gets the second item in a list. It says it gets the second item in a pair, which is right. The tail of a list is just the second item in the first cons cell of the list, which is why cdr gets the tail of a list.

                    2. 3

                      I’m pretty sure cdr means “contents, decrement register”, whatever that is. I think it’s just some register they started using for this purpose when they developed lisp.

                      1. 1

                        totally agree ; thanks too

                    3. 5

                      You can encode pretty much whatever you want into functions. Here’s the natural numbers, for instance

                      function fromNat(n) {
                        if (n == 0) { (s, z) => z} 
                        else { (s, z) => s(fromNat(n-1)(s, z)) }
                      }
                      
                      function toNat(n) {
                        n( (x) => x + 1, 0 )
                      }
                      

                      There’s even an automatic way to do this encoding to any algebraic data type.

                      1. 3

                        Another strange beast I found somewhere is this

                        (define true (lambda (x y) x)))
                        (define false (lambda (x y) y)))
                        

                        But how do you use it?

                        ;; definitely not working but you get the idea
                        (define-syntax if
                          (syntax-rules ()
                            [(_ t l r)
                             (t l r)]))
                        

                        So with just function application you can do a lot! Abstraction for data and control flow!

                        In contrast you can get something also delightful from go boolean builtin

                        const (
                            true  = 0 == 0 // Untyped bool.
                            false = 0 != 0 // Untyped bool.
                        )```
                        
                        1. 3

                          That’s pretty cool, thanks for sharing :)

                          EDIT: Wow straight up I just used this trick for a test I’m writing for work. Python library uses *args and instead of fighting trying to fit an extra member into the object, I just closed over the thing I was trying to pass in.

                          1. 2

                            can someone explain to me what is so mind blowing?

                            1. 3

                              I was trying to get across this sense of wonder that I had while watching the SICP lectures.

                              Throughout the book and lectures you use cons cells to build up abstractions, and then use those abstractions to build up other abstractions. Eventually you have multiple layers. For example, you may cons together numbers to represent a point. And then cons together points to represent a line. And then cons together lines to represent a polygon.

                              At some point the authors/lecturers point out that cons itself could be implemented using nothing but procedure calls. When doing that, we end up with this whole tower of abstractions and not a single “concrete” (not sure if that’s the right word or not) type. All we need are procedures/functions.

                              Maybe this was more mind-blowing to me than it should’ve been, but it felt like opening my eyes and seeing the world a little bit clearer.

                            2. -2

                              Would someone care to explain as to exactly why this blew his mind? I do not understand Scheme enough to see/understand what is happening here.

                              1. 3

                                A cons cell is like a tuple or two element list in other languages. The straightforward way to implement it in Javascript would be using arrays. However, you can use other types of containers.

                                In the second case, the author is using closures to hold the values. Cons returns a function that has closed over the values a and b. It also takes a function as a parameter, applies it to the values a and b, and returns the result. This gives you a way to work with the elements in the cons cell. getFirst and getSecond pass in a function that extracts their respective element.

                                Does that make sense?

                                1. 2

                                  Yes, more or less, I now understand the significance a bit better as how this would blow someones mind. Thank you for explaining!

                                  1. 1

                                    Awesome! Glad it was useful.