KludgeCode

is Ben Rudgers

From OOP to Lisp: Part A

In a OOP-langp tutorial – I am using psuedo-C# – we might be given an example to set up a banking account for an account object with the following code:

   Account myAccount = new Account(100);

If we  needed fifty bucks we would expect to use something like:

   myAccount.withdraw(50); 

and if we attempted to get a grand, we would expect an error message to be generated from the call:

   myAccount.withdraw(1000);

Likewise

    myAccount.deposit(1000); 

after the first withdrawal would be expected to tell us that we were thousandaires with $1050 in myAccount.

All this is pretty much standard fair, but what does it look like in a Lisp [scheme] without using objects? Well it starts off pretty similar:

   (define myAccount (make-account 100))

And if we can get past some minor points of syntax, withdrawing fifty bucks and depositing 1000 look pretty close:

   (myAccount 'withdraw 50)
   (myAccount 'deposit 1000)

And this is surprising, at least to me, because I always imagined that OOP relied on voodoo and mojo until I came across this example in Structure and Interpretation of Computer Programs which instead of being built on top of an object oriented language, is just made up of normal Scheme like this:


(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)

Where is the paradigm? The whole thing is built out of air and define from normal Scheme in fifteen lines of proper formatting. The equivalent as a class definition in C# is twenty three line – seven of which carry only a brace so it is of equivalent length:


class Account
{
    private decimal balance;
    public Account(decimal amount)
    {
        this.balance = amount;
    }
    public decimal withdraw (decimal amount)
    {
        if (balance >= amount)
        {
            balance -= amount;
            return balance;
        }
        Console.WriteLine("Witdrawal attempt failed.");
        return balance;
    }
    public decimal deposit (decimal amount)
    {
        balance += amount;
        return balance;
    }
}

I should have seen this coming when Ableson and Sussman first mentioned constructors and selectors – I just hadn’t bought into the whole conjuring and magic idiom yet. But this morning I reread

(myAccount 'withdraw 50)

and realized that but for two extra spaces characters and swapping the period for a quote,* it’s an anagram of:

myAccount.withdraw(50);

.
I'm thinking two extra spaces is a small price to pay for not having to swap paradigms. Now all I need to do is get the image of a naked wizard behind the curtain out of my head.

[Edit] After previewing this post, what is strange is how much more compact the Lisp constructor looks to me compared to the C# version. It just looks dense, like a book. Maybe that's why only certain people are attracted to it?

*And a semicolon! How could I forget the semicolon?
 

Learning to Read Lisp: Part A

I’ve always struggled to look at Lisp and figure out what was going on, and so I was working through Quick: An Introduction to Racket with Pictures in preparation for the upcoming course Introduction to Systematic Program Design at Coursera [it starts June 3rd 2013] when I typed in this code:

  (define (series mk)
      (hc-append 4 (mk 5) (mk 10) (mk 20)))

and I went “huh?”

My next thought was, I should rewrite it like this so that I understand it:

  (define (series mk)                        ;Series takes a procedure as its argument
      (hc-append 4 (mk 5) (mk 10) (mk 20)))  ;mk is a procedure

I often feel a need to add this level of comment to any code I write. It was only later, as I was walking the dog, that I realized how unnecessary the comments are in this case – (mk 5) gives the game away. It’s position as first element in an unquoted list means that it has to be a procedure.

This does not mean that the converse – that position insures us that something is not a procedure – is true:

  (define (pork pie)
      (series pie))

So maybe I am back to all my comments. But at least now there are cases where I can read someone else’s code more fluently.

Remarks: Epigram 12

This is part of a writing exercise around Alan Perlis‘s Epigrams in Programming.

Recursion is the root of computation since it trades description for time.

When I first encountered this epigram my understanding of recursion was limited to recursive descriptions of procedures, not to recursive patterns of execution or recursive definitions of data structures.

The general pattern of recursive descriptions* I see in Lisp is:

  (defun foo (list 1)
         (let x (car list 1)
                (if (null x)
                    nil
                    (progn
                      (bar x)
                      (foo (cdr list1))))))

Unsurprisingly, given that Lisp was conceived to explore recursion in Turing machines, there seems to be an intimate relationship between <code>cons, car, cdr</code> and recursive definitions. Because recursive description takes time out of the equation, there is one less thing to think about when considering Turing Machines and their equivalence.

The cons abstracts space, recursion time.

*Given optimization there is probably a bias toward tail calls when possible. Given the limited resources of early Lisp systems, the potential for optimization may have contributed toward this bias.

 

Remarks: Epigram 11

This is part of a writing exercise around Alan Perlis‘s Epigrams in Programming.

If you have a procedure with 10 parameters, you probably missed some.

A plot device involving ten characters may invovle a Balrog; consider the side effects of Balrogs before incorporating such a plot device.

A procedure requiring ten parameters may be a sign that the procedure is highly dependent on program state . If highly dependent on state, it is likely that ten parameters do not capture the entirety of the relevant state. In cases where the parameters are not capturing state but values, then better data abstractions are called for – obviously the values are related, otherwise the procedure would not be using them together.

There are command line programs which have many optional parameters – but optional parameters would seem to be a different case – since what is optional cannot be missing in the sense the epigram implies.

Our intuition  is that these parameters are keyword associated – and tend to denote “helper” functions which either modify the interpretation of the input or format the output. Unlike the ten parameter procedure, the source for these options is the user’s mind, not the programmer’s – they are selected at run time, not compile time. The program has to ask the user what they want, there is no other way to read her mind.

Ten parameters for a programming construct is analogous to  our program is asking us for data. Instead it should read what is already there. It speaks to a mismatch between our data structures and our procedures. They are not mapping together well. Or it means that we are not controlling state but still depending on it.

The user space view and the programmer space view overlap in areas like SQL queries. These may be constructed from multiple arguments.  But like keyword arguments on the command line the elements are built up and optional.

Remarks: Epigram 10

This is part of a writing exercise around Alan Perlis‘s Epigrams in Programming.

Get into a rut early: Do the same process the same way. Accumulate idioms. Standardize. The only difference (!) between Shakespeare and you was the size of his idiom list – not the size of his vocabulary.

  Aye Whores! Aye Whores! My kingdom for the whores![1]

Prostitutes plied their trade among the gin drinking garlic sucking rabble in the pit of the Globe – their solicitations undoubtedly approaching climax in Act V, Scene IV just prior to the gore of Richard III‘s death. Shakespeare managed state better than me, too.

Five acts for Julius Caesar. Five for The Taming of the Shrew. Poems in 14 lines. That’s standardization.

When he needed to convey a particular idea with a particular rhythm, he sought out English words, when he couldn’t find an English word he did pluck one from French, Russian, or Finnish – he created a new English word.

There is no Ook! sonnet. Idioms are different from the sum of their parts. Some provided advantages. Others are detrimental. Thus efforts to avoid spaghetti code – usually. Sometimes it is a reasonable trade-off all things considered. Idioms are measured against a combination of procedures and data structures and real world concerns.

A function which treats a tree like a tree is not idiomatic. A function which treats a list like a tree can be a useful idiom because we recognize the tradeoffs. Shakespeare could use plays within a play because his audience was sophisticated enough to get the point. They didn’t go home [with a prostitute?] when Hamlet’s play [Shakespeare’s idiom] ends in chaos.

Meanings are not just out their floating about in the world. The idiom of the theater is used to communicate mental states of one character to another – Shakespeare also understood recursion.

 

[1] Among my many first semesters back in school, I took a class from the Shakespearian scholar Sid Homan. I made a C+. I was even worse as a writer then than I am today. But I learned a lot. I’ve taken some artistic license with my memory of his lectures.

The Problem with SLIME

A programmer wants to learn Lisp, now he has two things to learn. Lisp and Emacs. [1]

I no longer feel lost in Emacs. Once I started reading EmacsRedux, I was able to anchor it to my background with AutoCad – and there are huge architectural similarities between Emacs and pre-Windows versions of AutoCad.  Importantly for my learning process, both have a command driven interface.

The difficulty with the Emacs tutorial is that it is written around social mores. Thus it teaches the shortcuts which distinguish emacs community insiders from outsiders [2]. Yes, no sane experienced Emacs user will type:

  M-x next-line

But it turns:

  C-n

from cryptic into mnemonic. That’s where beginners start.

 

[1] If your on Windows and aren’t afraid of learning Emacs, I strongly recommend LispCabinet. If you don’t want to learn Emacs, then LispIDE has the advantages (and disadvantages) that stem from being lightweight. Both work fine under Windows 8.

[2] http://learncodethehardway.org/blog/AUG_19_2012.html

Remarks: Epigram 9

This is part of a writing exercise around Alan Perlis‘s Epigrams in Programming.

It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures.

Data structures are the characters within the story told by the program. Functions are the events those characters encounter. Have a protagonist. Make events relevant to the protagonist.

One data structure is chosen over another for efficiency. But the efficiency of a data structure is not intrinsic. It is a function of the methods used upon it balanced with the role of each method within the critical path of the program. Introducing a new data structure for the sake of speed can only make sense if the data structure and function combination is used in the critical path and the critical path is driven by performance considerations.

Tolkein wrote himself into a corner when he placed his protagonist at Mount Doom with no ring and a long way from home. But he didn’t restructure his story. He bashed the tale in place using:

    hobbit.eagles(characters)

It’s not beautiful enough to please a literary critic, but it works well enough for n epic. It’s a good example of reuse. Even if another journey across the plain of Mordor would make more logical sense, who’d want to read about that?

The tuple is the protagonist of LAMP and other software stacks. 100 or 1,000 query functions may be handled with aplomb. A program may let the user create arbitrary queries without undue concern. Even placing CRUD in the user’s hands is not out of the question – there is only a single set of safety protocols which need to be implemented.

If a second data structure is added, however, we must implement the protocols again. If a third data structure is added…etc.

So for expediency, the most likely outcome of multiple data structures is removing or limiting the power given to the user. The second most likely option is to provide different degrees of access depending on the level of effort put into programming the methods for user access. This is a source of frustration when the user legitemately anticipates a uniform level of power but is thwarted by a difference in access arising solely because of differences in data structure.

This was my experience with The Argument Button. An architectural decision created opacity where transparency was anticipated.  Like LAMP start with a single persistent data structure. Nobody cares what you do behind the scenes temporarily, but multiple persistent data structures create opacity or foster limiting the user’s power.

Remarks: Epigram 8

This is part of a writing exercise around Alan Perlis‘s Epigrams in Programming.

A programming language is low level when its programs require attention to the irrelevant.

Perlis is treating languages as cultural artifacts that change rather than a technology which develops – low and high are relative values assigned by individuals in light of their work not within a great chain of taxonomy. Languages evolve by adaptation. They are not trees growing as a function of light and air.

Low and high depend on state. C++ is high level if one needs to manage memory directly. But low if garbage collection is good enough – Rich Hickey observes that programmers adopted Java over C++ because managing memory was incidental complexity. Each workday they had business problems to solve plus memory to manage. High level languages reduce the swamp’s alligator population.

>"hello world"
HELLO WORLD

In this sense, object oriented languages like Java may be seen as low level because they require implementing objects all the way down to Hello World. What makes a language low level is not missing features, but the amount of work a particular language requires of a particular programmer for a particular project at a particular time.

By extension, any software is low level when it leaves the user dealing with irrelevancies regularly. Ignorance plays a role. Ubuntu defaults to manually approving updates, then requires a password. There is a setting somewhere, but looking for it is not what I am trying to do, updating is not what I’m trying to do either. The irrelevancies pile up. This is not unique to Ubuntu. The same could be said of any OS.

The way in which the low level can be raised to high level in software is by providing fine grained configuration control. Sometimes I may need to allocate disk sectors directly. Usually I don’t. The ideal high level program hides and displays features at the appropriate time [I am reminded of the Ribbon interface].

In this analysis, configuration options become a domain specific language. The more powerful and flexible the configuration options are the more powerful and flexible the software can be. Because everything in the configuration options is relevant to the program, one could begin designing a program with them. It would be an interesting exercise; one based on the question, “What power am I going to give the user?”

This seems to me to be the story of Emacs and its evolution. Emacs is little more than a text editor with extremely fine grained configuration options – so fine grained that the configuration options have their own Lisp. Then again, between the keyboard and the screen what else should there be? The same could be said of AutoCad. Configuration -> Automation.

Over time, what is irrelevant changes as technology evolves. Memory management has become less relevant (from the perspective of the typical programmer) as more transistors can be placed on a chip. Type safety has become more relevant as larger and larger programs move onto more diverse machines. We don’t want javascript controlling the brakes on a truck.

By Perlis, Javascript is more high level than Java much of the time precisely because it doesn’t require explicitly creating classes or impose type safety and the applications it is used for don’t require either. One might say that the more idiomatic the requirements of a language the lower level it is.  Lisp and Javascript have idioms, but a programmer doesn’t have to use them. C++ has idioms that cannot be avoided. Required idioms are imposed by syntax.

 

 Page 4 of 8  « First  ... « 2  3  4  5  6 » ...  Last »