Tuesday, August 30, 2011

Point-free programming in Smalltalk

Smalltalk does not, ordinarily, support point-free programming. Point-free programming is programming without "points", or variables. In a loop, rather than assigning every item to loop over to a variable, you only write which function should be called on them.

Pharo Smalltalk has limited support for that. There, you can write:


#(1 2 3 4) select: #even -> #(2 4)


The above is implemented using an easy equivalence. Every block that sends only one message is equivalent to the selector of that message. Thus, the above is equivalent to:


#(1 2 3 4) select: [:e | e even] -> #(2 4)


Which is just standard Smalltalk. The equivalence can be made work by implementing #value: etc. on Symbol.

Well, of course this is rather limited, we still haven't got rid of blocks. We still need blocks for everything that does not only send one message. You can push a little farther by allowing composition of symbols:



{'100' . '20' . '3'} sort: #<= * #first. -> #('100' '20' '3')


The idea is to read the asterisk as "after", like the function compositor in Mathematics.

So you read this as "sort the area by less than, after first was applied". Thus, the strings are sorted by their first letter. Awesome, eh?

This is not standard Pharo, but it can be implemented in a couple of minutes. The code is on Snipt, but it's more easily written than read.

This can still not replace blocks. For example, the following block still cannot be expressed point-free:

[:a | Transcript show: a]
.

But nothing is easier than inventing a syntax for that (can you implement it in Pharo, so it'll work?):

 Transcript delayedSend: #show: 


Finally, for blocks where the left-hand-side is the argument:

 [:stream | stream nextPutAll: 'hello']


I propose that could be written as follows (can you implement it in Pharo, so it'll work?):

Delayed nextPutAll: 'hello'


Open questions


Would it be more enjoyable to write entirely point-free in Smalltalk? What do you think?

8 comments:

  1. The problem with your last examples is that you need parenthesis such as in:

    #(1 2 3) do: (Transcript delayedSend: #show:)

    the advantage becomes not that clear compared to the standard:

    #(1 2 3) do: [:a | Transcript show: a]

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Check the blog of Vassili Bykov from 2007:

    - Selectors as Blocks: http://blog.3plus4.org/2007/03/27/selectors-as-blocks/
    - Sections: http://blog.3plus4.org/2007/03/31/sections/
    - Sections Wrap-Up: http://blog.3plus4.org/2007/04/06/sections-wrap-up/

    And a similar idea from 2000:

    - http://live.exept.de/doc/online/english/programming/humor.html

    ReplyDelete
  4. Except of making your inner mathematician happy, what’s argument in case of “point-free programming?” Has it been proven to be more readable? Less error-prone? leading to less bugs? is there any argument in favor or pointless programming that is based on a sound understanding of the cognitive challenges of programming?

    In the first place, what is broken about blocks?

    Nothing.

    Except that simple blocks add the cognitive burden of choosing a variable name that is only used once. Yet, there is better ways to address this issue than fucking up the way blocks are written. Groovy has a very elegant solution to this, for any block if no argument names are given the first argument can be accessed as “it.” Cognitive problem solved! :)

    ReplyDelete
  5. PS, maybe I should add that I like your language experiment very much and that this is what makes Smalltalk so fun ... you can quickly prototype and play with a new language feature, just like ... so keep experimenting ... looking forward to more blog posts like this one!

    ReplyDelete
  6. @Adrian: There is something wrong with blocks, but I won't claim the above is even close to a solution. In chapter 7 of Making software, (http://oreilly.com/catalog/9780596808303), I learnt that people don't naturally express set operations on the elements of the set. That is, #(1 2 3) squared is much closer to natural language than #(1 2 3) collect: [:each | each squared].

    I wish for some (related) language features:

    * First-class collections. Collections should not be standard objects, and various 1:n relationships should not be modeled using collections.

    * Dimensions. In natural languages, nouns have dimensions, and "behave" different according to those. This makes sense for programming languages, too. This might just be the previous point again: maybe first-class dimensions understand that right away.

    * To control dimensions, use adverbs. If you ask a set to do something, you should specify which dimensions you expect for the result.

    Some of that is implemented in J (http://www.jsoftware.com/), or sly3: (http://www.stefan-marr.de/renaissance/sly3-overview/)

    But I'm still not quite happy with either. But that's another blog post :)

    ReplyDelete
  7. Maybe Higher Order Messaging could be viewed as a point-free progamming mechanism?

    See the oopsla 2005 paper that can be found on this page: http://www.metaobject.com/Research/

    ReplyDelete
  8. F-Script, a Smalltalk dialect, provides both selectors as blocks and an APL-inspired point free programming model: http://pmougin.wordpress.com/2009/11/17/beyond-blocks/

    ReplyDelete

Note: Only a member of this blog may post a comment.