This article introduces my comparison between Scheme – a functional programming language, and Smalltalk – an object-oriented programming language. The comparison is to help me understand simplicity and beauty of both languages I have learnt.

1. Common characteristics

1.2 Minimal language

Firstly, both languages offer a very simple syntax. While Scheme programs mostly contain functions and macros with parentheses, Smalltalk programs are comprised of messages and objects. Moreover, in general, ev- erything in Scheme is lambda expression with returning values, and in Smalltalk is object with methods and variables.

However, Smalltalk language is verbose compared to Scheme, but it is a good tradeoff because Smalltalk is more easy-reading than Scheme.

1.2 Type systems

1.2.1 Dynamic typing

In general, a dynamic typing language is the language that checks types of variables at runtime. In Scheme, we have both global and local variables, and similar to Smalltalk, it is dynamic typing. Suppose we want to define a global variable uglyNum , assign it value 99 , and then make an arithmetic computation with it.

> (define uglyNum 99)
> (+ uglyNum 1)
100

In the code above, we did not specific any data type for uglyNum  but still can make computation with the variable. However, if we try to combine uglyNumber  with a string, an error will be throwed. Or we even can create the function foo  without any problems as long as we should never invoke the function.

uglyNum 99
uglyNum. -> 99
uglyNum ’Hello Smalltalk’

Nevertheless, Smalltalk actually does not has types. Remember that everything in Smalltalk is object, and in our case, we simply refer variable uglyNum  to the integer 99  or the string ‘Hello Smalltalk’ . Thus, we can say Smalltalk is dynamic typing or untyped in terms of the variables.

1.2.2 Strong typing

Scheme is a strongly typed language, which means we can not convert from one type to another. Therefore, error is prevented at runtime.

;;; It will cause an error in Scheme
> (+ 1 "1")

In case of Smalltalk, variables can have dif- ferent types at different moments, but objects do not. Types of objects is always strong. ’Hello World’ , for example, is always an instance of class String ; or 100  is always an instance of class SmallInteger.

1.2.3 Duck typing

Smalltalk provides duck typing happening at runtime. The behaviors of objects are called by sending any messages to the objects. Thus, if the object understand the message we send to it, we can assume its type even we do not know what class is involved.

1.3 Lexical binding

Like most modern languages, Scheme and Smalltalk both use lexical scope. The language supports lexical scoping, which means scoping organized in a structured hierarchy, and the innermost will be used in case there are several bindings in the environment.

In Scheme, we can use let , let* , letrec  to declare and bind local variables.

;;; bind x to separated scopes
> (let ((x 1))
    (let ((x 2))
      (print x))
(print x)) 21

 1.4 Closure

Scheme. Procedures, called closures, can record its surrounded environment to use it in later computations.

>(define (plus x)
(define (plus-x y)
     (+ x y))
   plus-x)
> (define plus1 (plus 1))
> (plus1 2) 3

As you see, somehow the plus1 automatically stay around the return of the function plus . Thus, when we pass the integer 2  to the function plus1 , it will gather up and make a final computation.

Smalltalk. Each block in Smalltalk is a full closure and also an object because every block is an instance of the Block  class. So we can always refer to the environment recorded by the block object as long as the block object exists.

Suppose we create a class MakeAdd  that creates instances have two variables x  and y . We will also define a method that will return the block of making sum of x  and y.

theplus <- MakeAdd x: 1 y:2.
theblock <- theplus getBlock.
blockvalue <- theblock value. 3
theplus x: 10 y: 20.
newblockvalue <- theblock value. 20

What we have done in the code above is creating an instance theplus  of the class MakeAdd  and initializing its variables x , y  with 1 and 2 correspondingly. Next, we assign the returned block to new variable theblock , and its value to blockvalue , then we modify the values of x  and y . Finally we assign the value of blockvalue  to new variable newblockvalue . When comparing the return values of blockvalue  and newblockvalue , we see that the object blockvalue  is able to update changes to its captured environment and then assign it to newblockvalue .

2. What’s different between Scheme and Smalltalk? 

In general, there exist many differences between Scheme and Smalltalk.

2.1 Programming paradigm

While Smallltalk is the pure object-oriented programming language, Scheme is functional-oriented.

Smalltalk. In Smalltalk, all variables are objects, which basically contains methods and variables. To invoke a method of an object, the programmer sends corresponding message to it with all necessary parameters. Strictly speaking, the only way to interact with objects in Smalltalk is through their sets of messages, called the interfaces.

Variables contain data of the object, which is private and only accessible through object’s protocol. They have the same attributes as variables in Scheme. For example, they have names and contain values, and are dynamically typed.

Objects are instantiated from classes. The class has its own methods and variables, which should be distinguished from methods and variables of instances. Moreover, instance variables are not shared among instances; instead they are strictly private to the corresponding instance.

Scheme. Scheme is a minimalist language based on Lisp. It is mostly functional. In a nutshell, there are two types of data in Scheme, primitive and list. Primitives are the simplest type, and cannot be divided. Some primitive data types are numbers, symbols, Booleans, strings and characters. Lists are far more complicated, and considered as the most important data type in Scheme. We can create a list from primitive data types and use it as specific unit for further implementations.

2.2 First-class citizen

In programming language, first-class citizens are things we can manipulate in all general ways. In most cases, we can pass first-class citizens as arguments of functions, or set the return them from the functions. As Scheme is object-oriented programming language, and Scheme functional-oriented, they has different kinds of first-class

Smalltalk. Both blocks and classes are first-class objects in Smalltalk. As said, we can store blocks in variables, pass them around or send message to them.

”send aBlockMessage to a block of sending aSelfMessage to self”
[self aSelfMessage] aBlockMessage

In some cases – such as enumeration – we can pass more than one block to a method.

” the method detect: ifNone: takes two blocks”
user detect: [ :user | user name = ’Ngoc

Similar to the block, Smalltalk classes can receive messages. We can say one of the most frequent message sent to classes is new. However, classes requires specific messages, which we call class methods.

”Create a new instance of the class Banana”
banana Banana new

Scheme. In Scheme, functions are considered as first-class, which means we can pass functions as parameters to other functions.

> (map abs '(1 2 3 -1 -5))
(1 2 3 1 5)

2.3 Lambda calculus and block 

Scheme leverages lambda expression to create anonymous function. It is powerful and dominant when people can do most of the things in Scheme only by using lambda function. In Smalltalk, the blocks are closely equivalent to the lambda functions.

Smalltalk: [:a :b :c | … expression…]

Scheme: (lambda (a b c) (… expression…))

As said, we can pass lambda functions as arguments in Scheme, and in Smalltalk, we can almost do the same. For example, map- like operation such as select:  as below.

Smalltalk: (1 to: 3) collect: [:x | x + 1].

Scheme: (map (lambda (x) (* x 2)) (list 1 2 3))

Finally, compared Smalltalk’s block to lambda function of Scheme, lambda function is dominant as we are able to use it to create recursion functions.

3. Unique features of the languages 

3.1 Smalltalk

3.1.1 Smalltalk is reflective

Smalltalk is a reflective programming language and “has one of the most complete sets of reflective facilities” (Rivard, 1996). In a nutshell, reflection is “the ability of a program to manipulate as data something representing the state of the program during its own execution” (Ducasse et al., 2009).This capability, as a result, allows Smalltalk programs examine and modify their runtime stacks and redefine their methods. As said, we can only access instance variables by the instance accessors. However, Smalltalk introspection allows us to inspect the object, modify its instances and send message to it. In the example, we firstly use the message class to ask the class of declared instance student. Next, we use the message isKindOf: aClass  to verify if student is an instance of the classes Student , Person  and Float .

student Student name: ’Ngoc Tran’ address: Brussels.
student class -> Student
student isKindOf: Student -> true student isKindOf: Person > true student isKindOf: Float -> false

We can also send the message to the class Person  to ask its method names defined locally by sending message selectors to it, or ask for the class locally defined and inherited methods by the message allSelectors.

Person selectors.
Person allSelectors.

In addition to inspecting objects and classes, Smalltalk intersection provides the capability of modifying objects and meta-objects at runtime, or adding and removing methods. For example, suppose we create a class whose instances act as proxies for other objects. Any messages sent to the real receiver have to pass through the proxy instance, in which necessary implementations, e.g., logging, will be executed before messages are delegated.

In practice, reflection provides a framework to develop programming tools such as debuggers, or to change the programming paradigm.

3.1.2 Smalltalk has metaclasses

In reality, the developers not only need to access the values of application data at runtime but also inspect their types. For instance, in Java, when using cast, the developers mean to ask the type of the variable. With Smalltalk, there is such a beautiful way to solve this problem – using metaclasses.

Before continue this part, there are several important rules we have to make clear.

1. Everything is an object.

2. Every object has class.

By following the rules, we can say that every class is an object, and has its corresponding class. However, what is the class of class?

The answer is metaclass, which holds all methods of its class and will be created automatically behind the scenes whenever a class is created. For example, the metaclass of the class Person  is Person class . Furthermore, inheritance is also possible in metaclass. It is significantly important because all methods in super metaclass will be inherited by sub-metaclass. Suppose that the Person class  provide method name:  for the class Person , then the Student  class will inherit this method as the Student  class extends from the Person  class. More interestingly, if we want to find the super class of the Object  class, we will get an endless query.

Object class.
Object class superclass.
Object class superclass superclass.
Object class superclass superclass sup

So, what is the class of metaclass? The answer is Metaclass . As said, every object in Smalltalk is class, so Metaclass  is also an object, and thus it must have a class itself. What is the metaclass of Metaclass  class? The answer is Metaclass  class, and here is the interesting part, Metaclass class  is an instance of Metaclass . In short, it is an endless loop if we want to try to find out.

Indeed, metaclasses is dicult to grasp the whole concept but programmers do not have to understand it to use Smalltalk. And the parallel of metaclass hierarchy and the class hierarchy seems to be confusing but in real world, it helps creating elegant applications.

3.2 Scheme

3.2.1 Scheme macros

Macros is a beauty of Scheme by which programmer can construct new syntax. The macros are useful for many purposes. Using macros, we can hide complexity and avoid repeating ourselves, by which we can concentrate on high-level problems.

> (define (zpn-x
         value
         z-exp
         p-exp
         n-exp)
(cond
  ((zero? value) z-exp)
  ((positive? value) p-exp)
  ((negative? value) n-exp)))
> (zpn-x 1 (display "Z") (display "P")
   (display "N"))
ZPN

The result is not as we expected because three expressions are called even before the function zpn-x  is called. So, the function displays ZPN and returns null . The solution is to define a macro zpn-x  as follows.

> (define-syntax zpn-x
  (syntax-rules ()
((zpn-x
value z-exp p-exp n-exp)
(cond
  ((zero? value) z-exp)
  ((positive? value) p-exp)
  (negative? value) n-exp))))
> (zpn-x 1 (display "Z") (display "P")
   (display "N"))
P

 3.2.2 Scheme call/cc 

Scheme offers first-class continuation by call/cc  to “control the execution order of the instruction” (Wikipedia, 2013). In a nutshell, a continuation is such a lexical closure waiting for returned value of the current execution, and will resume the execution with the environment it wraps inside.

> (define x 0)
;;; use call/cc
> (+ 1  (call/cc
(lambda (cc)
(set x cc)3 )))

In short, the code above adds two numbers together – the integer 1  and the call/cc  – in the first parenthesis. Here we have a call/cc  takes one argument, a lambda. Simultaneously, the call/cc  captures the context in the first parenthesis, say (+ 1 []) , which is the continuation.

Here the lambda will be triggered for the continuation (+ 1 []) . In the lambda, we bound the continuation to x  and return 3 . Therefore, it yields the result 4 .

;;; x now holds the continuation
;;; and we pass the value 10 to x
> (x 10)
11

After we bound x  to the continuation, we pass the value 10  to x . This value will be use as the return value off the continuation. The return value is added to 1 . Therefore, we get the value 11 .

References

Beck, K. (1997). Smalltalk Best Practice Pat- terns. Volume 1: Coding. Prentice Hall, Englewood Cliffs, NJ.

Ducasse, S., Denker, M., and Lienhard, A. (2009). Evolving a reflective language: Lessons learned from implementing traits. In Proceedings of the International Workshop on Smalltalk Technologies, IWST ’09, pages 82–86, New York, NY, USA. ACM.

Rivard, F. (1996). Smalltalk: a reflective lan- guage. In Proceedings of REFLECTION, volume 96, pages 21–38.

Scharli, N., Ducasse, S., Nierstrasz, O., and Black, A. P. (2003). Traits: Compos- able units of behaviour. In ECOOP 2003– Object-Oriented Programming, pages 248– 274. Springer.

Wikipedia (2013). Continuation – wikipedia, the free encyclopedia.