KISS principle applied to programming language design

language-designprogramming-languages

KISS ("keep it simple, stupid" or "keep it simple stupid", see e.g. here) is an important principle in software development, even though it apparently originated in engineering. Citing from the wikipedia article:

The principle is best exemplified by the story of Johnson handing a team of design engineers a handful of tools, with the challenge that the jet aircraft they were designing must be repairable by an average mechanic in the field under combat conditions with only these tools. Hence, the 'stupid' refers to the relationship between the way things break and the sophistication available to fix them.

If I wanted to apply this to the field of software development I would replace "jet aircraft" with "piece of software", "average mechanic" with "average developer" and "under combat conditions" with "under the expected software development / maintenance conditions" (deadlines, time constraints, meetings / interruptions, available tools, and so on).

So it is a commonly accepted idea that one should try to keep a piece of software simple (or simple stupid, in case you omit the comma) so that it easy to work on it later.

But can the KISS principle be applied also to programming language design? Do you know of any programming languages that have been designed specifically with this principle in mind, i.e. to "allow an average programmer under average working conditions to write and maintain as much code as possible with the least cognitive effort"?

If you cite any specific language it would be great if you could add a link to some document in which this intent is clearly expressed by the language designers. In any case, I would be interested to learn about the designers' (documented) intentions rather than your personal opinion about a particular programming language.

Best Answer

When I think of minimialism, I think of Lisp and Go. With Lisp, all you have are functions and lists, which is about as simple as you can get (well, there's a little more, but whatever). However, I think the Go case is more interesting.

Go was designed to be simple (it's a decent read). The term they use is "feature orthogonality", which means that any feature should only be added if it provides something truly unique. This seems to stem with the authors' (Russ Cox and Rob Pike come to mind) involvement with Plan9, which was a reimagination of UNIX with simplicity in mind. (If you're interested in minimal design, Rob Pike's paper on a simple windowing system is a good read.)

Here's some simple examples of the syntax:

Only one looping construct

A loop can look like one of the following:

Infinite loop

for {
}

While loop

for <conditional> {
}

Traditional for loop

for i := 0; i < 10; i++ {
}

Foreach loop

// works for maps or arrays
for k, v := range arr {
}

Multi-purpose switch

switch {
    // cases must be evaluate to a boolean
}

switch <value> {
}

switch t := <value>; t {
    // can use t inside
}

Multiple return

return val1, val2, ...
  • Removes the need for throw (pass the error as last return value)
  • Removes the need for out parameters
  • Removes the need for tuples

Interfaces

type X interface {
    DoSomething()
    String() string
}
  • Solves similar problems as generics
  • Allows abstraction

Embedding

type A struct {
    Thing string
}

type B struct {
    A // embeds A in B, so B.Thing refers to A.Thing
}
  • Solves same problem as inheritance
  • Obviates need for classes

Channels

Can be used to implement semaphores

var c = make(chan bool, 1)
c<-true // semaphore lock
<-c // semaphore free

Used for message passing between threads

func produce(c chan<- bool) {
    for {
        c <- true
    }
}
func consume(c <-chan bool) {
    for {
        <-c
    }
}

var c = make(chan bool)
go produce(c)
go consume(c)

Can be used to handle asynchronous events

func async() chan bool {
    var c = make(chan bool)
    go doSomethingAsync(c)
    return c
}

// wait for long async process to finish
c := async()
select {
    case _ = <-c:
}

Conclusion

I'm not going to go into every part of the syntax, but hopefully you can see what minimalism can do. The language is intriguing not because it adds a ton of new features, but because it uses the best features from other languages without anything extra.

There's usually one "best" way of solving a problem. For example, on the mailing list, a lot of users complain about not having generics. After discussion, they realize that everything they want to do can be done with interfaces. Read up on effective go for examples on idiomatic syntax.

The benefit of KISS languages is it's possible to write idiomatic code, because code style is restricted by the language. For example, in Go, you cannot write something like this:

if <condition>
    statement;

You have to use curly-braces:

if <condition> {
    statement;
}

There are many other examples of this in the syntax, which makes reading other peoples code easier.

Benefits of KISS language over featureful languages:

  • easier to understand others' code
  • easier to grok the entire language (C++ is notorious for being hard to understand)
  • focus on algorithm, not the syntax
Related Topic