KISS principle applied to programming language design?

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.

24

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

4

I think the Zen of Python will state why Python is a simple language a lot better than I can:

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Edit in response to @Giorgio

As your question states,

“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'”

Python is, to me, what immediately comes to mind. Python’s design is in direct response to Perl’s methodology, the famous “There’s More Than One Way To Do It”. While that’s great, allowing programmers to very easily write code, it doesn’t help with maintenance. Having before managed a (very poorly written, I’ll admit) program written in Perl, I appreciate Python forcing both good coding practices and concise language down your throat.

Python also does not force you to follow one particular methodology when writing programs. I can choose to follow strict Object-Oriented programming style, or I can write a simple script to execute sequentially. Not having to initialize a class, then call the main() method like you’re forced to do in Java is very nice. (Also, printing to stdout in Python is wonderful, but that’s a rather null point).

Finally, the “Batteries Included” methodology of including an expansive standard library with the language is wonderful. Instead of hunting through some external repository for packages, most of what I need is already included in the language. It’s also nice having that external repo, but not having to dig through it in order to do a basic operation is really handy.

5

A major key to maintaining “simplicity” is to recognize when complexity will be necessary, and have the parts of the system that can best deal with it, do so. Having a single kind of object reference which makes no distinction between immutable values, mutable values, and entities, makes Java “simple”, doesn’t eliminate the need to distinguish between values and entities; it merely deprives programmers of tools to help them do so.

More generally, if one is trying to have a programming language or framework feature support multiple usage cases, one should ensure that there won’t be any situations where different behaviors would be appropriate in different usage cases, but the compiler would be unable to tell them apart. Adding more types to a language or framework may seem like a complication, but in many cases it may actually make things easier.

Consider, for example, the mess of rules surrounding signed and unsigned types in C. The complexity of these rules stems from the fact that some code uses unsigned integer types to represent numbers, while other code uses them to represent members of a wrapping algebraic ring (specifically, the set of integers congruent mod 2ⁿ). The type conversion rules behave in ways which are sometimes appropriate to one usage, and sometimes in ways appropriate to the other. Having separate wrapping and non-wrapping types would double the number of integer types, but could simplify the rules associated with them:

  • Any non-ring integer type of any size may be assigned to a ring of any size; a ring may be assigned only to a ring of equal or smaller size.

  • Operations other than relational operators involving a non-ring integer and a ring will implicitly convert the integer to the ring type.

  • Rings may be explicitly cast to numbers or to larger rings; the value of an unsigned ring is the smallest non-negative integer which, when added to the ring’s zero, would yield the ring value. The value of a signed ring is the smallest-magnitude integer which, when added to the ring’s zero, would yield the ring value, with the negative value being preferred in case of a tie.

  • Except as mentioned above, rings are limited to operations with rings of the same size or smaller.

  • Operations on non-ring integers of different types should upcast the numbers to a type that can accommodate all values of either operand, or should be rejected by the compiler if no suitable type exists

Defining ring types would seem to double the number of integer types, but would greatly simplify the writing of portable code. Using the same unsigned types for numbers and rings reduces the number of types, but leads to complicated rules which make it almost impossible to write efficient portable code.

2

Arguably, Tcl was developed along these lines. The entire language can be described in only 12 rules on a single man page. It is remarkably simple and consistent.

The creator of Tcl claimed the following as the original goals:

  • The language must be extensible: it must be very easy for each
    application to add its own features to the basic features of the
    language, and the application-specific features should appear
    natural, as if they had been designed into the language from the
    start.
  • The language must be very simple and generic, so that it can work
    easily with many different applications and so that it doesn’t
    restrict the features that applications can provide.
  • Since most of the interesting functionality will come from the
    application, the primary purpose of the language is to integrate or
    “glue together” the extensions. Thus the language must have good
    facilities for integration.

From “History of Tcl” – http://www.tcl.tk/about/history.html

If a language is fixed in its design because of KISS, it cannot grow. A language that cannot grow will die.

In the following video Guy Steele cleverly explains that a programing language must be allowed to grow and why. If you apply KISS, then how can a langauge grow because once released, it’s set of tools is fixed and never allowed to change.

Guy Steele’s keynote at the 1998 ACM OOPSLA conference on “Growing a
Language” discusses the importance of and issues associated with
designing a programming language that can be grown by its users.

It is an hour long video but worth watching. If you don’t know who Guy Steele is you really should when talking about language design.

I choose this video as the answer because I believe that applying KISS to language design in general is wrong, and hope that seeing a speech from a noted person of language design will help to expand your understanding of the future of language design.

Since you came here to learn about language design and I gave you a reason not to use KISS it is only fair that I point out something that I find of help in language design. Cognitive dimensions of notations

EDIT

When I wrote the above answer it is was based on the reasoning of: if an engine can be only maintained with a fixed set of tools then my reformulation of that meaning with respect to language design is that a language cannot change once released. The comments are indicating that that was not what was meant. So let me put forth this modified answer that will be more in-line with the revised understanding.

If one takes a look at Cognitive dimensions of notations then one learns that there are many competing dimensions associated with language design than just simplicity and if you focus too heavily on one, you will suffer in others. With the question of is focusing heavily on simplicity (KISS) good, and backed up by noted people of language design, I submitted the speech by Guy Steele to show that trying to keep a design solely simple will impact other dimensions. More importantly I am trying to convey that you need to look at many dimensions and weigh the pros and cons of them, not just simplicity.

9

I am surprised that no-one mentioned C yet, even though it puts simplicity first in several important ways, oftentimes in such a radical way that programmers fail to appreciate the simplicity:

  • There is no hidden magic. If you write a + b in C, you have the guarantee that it will compile to a single assembler instruction at most.

    Even in relatively simple languages like Java, a simple statement like a + b may take several microseconds (if the variables are strings). Other languages add much, much more to this with the extreme example of C++ where a + b may be overloaded to make pink elephants appear. Not so in C: If it’s not a function call, it won’t take more than a few nanoseconds. This predictability of performance is one of the key features of C, and it’s certainly a form of simplicity.

  • The data types are as simple as they can be while still describing all the interesting data structures that you might want to build in memory: There is only the fundamental types a CPU can handle + aggregation (struct) + repetition (arrays) + references (pointers).

    It is true that the various lisp-based languages are much simpler than C in this regard. However, they do not try to allow the programmer to freely manipulate memory as C does.

  • Orthogonality of features: You can freely combine the features and they will work together as expected. Many languages fail this by allowing certain constructs only to appear in defined contexts.

    Take for example variable length arrays in C and C++: C allows runtime lengths everywhere while the most liberal requests to expand the C++ standard allows them only in certain contexts like automatic arrays and even there only in the first dimension. This allows C to handle true multidimensional arrays with sizes known only at runtime, whereas C++ programmers are reduced to write data[k + (j + i*lineLength)*lineCount] over and over again.

    Another example of this is the pointer: A data pointer in C this really is nothing more or less than just a variable holding a memory address of some other variable. Since the pointer is itself a variable, the double pointer is a well-defined thing. I.e. given what int and int* are, it is clear what int** must be. Compare this to C++ references where you have absolutely no clue what int&& is given the meaning of int and int&!)

It is true that it is precisely this simplicity that has earned C so much hatred: The language’s simplicity allows the programmers more freedom than is good for them, leading to a lot of problems with undefined behavior and mishandled pointers. Especially the simplicity of orthogonality that allows a programmer to define a variable as int (*array[m])(struct foo* (*arg)[n]) is of the type that tends to give readers headaches because really complex stuff can be expressed in very concise form by liberal combination of a few simple features. This is the type of simplicity at which C excels, and which give it the reputation of being a hard language to use.

Also, the language’s simplicity forces the programmer to write a lot more code than more feature-rich languages, providing more fertile ground for bugs to flourish. Nevertheless, it remains the simplest possible high-level language if your primary objective is to program a real computer and not some virtual machine.

3

Here’s a big quote from Bruce McKinney’s Hardcore Visual Basic, which in turn puts words into the mouths of the designers of BASIC, Kemeny and Kurtz. Emphases mine.

Every computer language has its own feel, its own atmosphere, its own
spirit. You can’t really define this spirit, but you know what it is
when you see it. I think of Basic as the antithesis of a statement
attributed to Albert Einstein:

Make things as simple as possible—but no simpler.

Had that quote been written by the original designers of Basic, John
Kemeny and Thomas Kurtz, it would have been simplified further:

Make things simpler than possible.

That is the contradiction hardcore Visual Basic programmers live with.
We want things to be simple, elegant, and intuitive—but they aren’t.
We want our programs to model reality—but they don’t. We want our
language to work the way we think, not the way computers or operating
systems want us to think—but we’re not willing to pay the price
.

2

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”?

It is a good thing you clarified what you mean by “simple”, because in my mind a simple programming language is one with minimal syntax and few features (Scheme, Forth, ML), which does not directly translate to your definition. I think you are really looking for languages design with RAD (rapid application development) in mind, and there are quite a few of them. See this StackOverflow thread, for instance: https://stackoverflow.com/questions/66227/what-is-the-best-multi-platform-rad-language

1

Java Wikipedia – although not explicitly stated in Wikipedia, simplifaction is mentioned a number of times and was an original design goal, as the only serious OO capable (Term used loosely) language in those days was C++, which as we all know, is powerful but has more than a few traps for the unwary.

The JVM was intended as a way to simplify cross platform development, and “Write once Run anywhere” became a Java mantra for a few years – again, the intent was the framework was simple to deploy.

I believe C# falls into that category of simplification of the language.

5

Hm… this is actually a tough question to answer ‘positively’, because when I think of simplicity in programming language design (and what’s ‘easier’ to work with), I immediately think of:

  1. “Readability” of code – how well the language encapsulates objects, methods, etc.
  2. The consistency of the language APIs and interfaces.

There are a number of languages that do these things well – but what pops into my head is a language that doesn’t do things well ‘as a programming language’: PHP.

[Disclaimer – I’ve written PHP and PHP is still my ‘go to language’ for simple web projects. This is a criticism of love…]

First, PHP does well for the environment that it typically runs in – web servers. It’s easy to set up, easy to maintain, etc.

Where I struggle with PHP: when you want to do something “unusual” – something that you typically don’t do on a regular basis (in the case I’m thinking of it was consuming a SOAP web service – not something I’ve done a lot with PHP), you’re confronted with two options:

1) Various open source extensions to PHP. The PHP object model is loose enough where interface design is also not terribly consistent from library to library, developer to developer. When confronted with a set of code where someone used a library that you’ve never heard of, you spend a lot of time figuring out ‘what the heck this does’ because the language allows for loose API / library creation.

2) The “built-in” functions – of which there are a lot. I’ve gone through a few rabbit holes of either finding another library or implementing a bit of functionality to somehow later on stumble onto impossiblyfound_basefunction()

In my opinion, simplicity is consistency.

Now, KISS approach to programming languages is a funny notion, at least if you consider programming languages to be an abstraction layer to a CPU’s instruction set, etc. If you do not define “KISS” more closely. I am just saying here, that an oxcart is KISS applied to a car to its fullest.

Now another interpretation of KISS could be something like “smartly done without needless ornaments and not overly complicated”. Especially one could argue, that there should not be too many specific cases for similar things, etc. Usually math is quite good at boiling down stuff to its essence, and – o wonder – mathematicians have spent some time thinking about programming and computers as well.

For programming, 2 quite famous abstract models exist:

  • One is the turing machine which defines a machine and a simple instructional model that is able to compute everything a computer could do.
  • The other one is the Lambda-Calculus by Church et. al. that is equivalent in power

Fun thing is: While the turing machine is quite simple in its layout, it is not a system that is easy to handle and I do not think it qualifies for “smart KISS”. But Lambda Calculus has more to do with programming languages that we know — and with Lisp and Scheme pioneering features of the lambda calculus it has made its way into a lot of languages.

Lisp and Scheme are REALLY simple, at least syntax wise. Syntax is a major problem with programming languages (which is probably why they are reinvented all the time). In the case of C++ it is almost unhandable for the human brain to predict how some source lines are interpreted by the compiler.)

Lisps are reducing the syntactical complexity altogether by introducing one common form for commands:

(command param1 param2 ...)

This can be a method call, such as

(max 1 2 3 4)

as well as a if branch, loop etc.

(if (< 1 2) 
    (write 4)
    (write 5))

(all code here is pseudo-Lisp/Dialect agnostic)

The form

(command param1 param2 ...)

can also be interpreted as a list

(item1 item2 item3)

And that is the basis for Lisps simplicity and beauty. Because nested lists (like in the if statement example) constitute trees and those can be easily understood both by the machine and by the human in front of the machine.

Another feature of Lisps are macros I won’t go into the dirty detail here, but since there is no syntactic difference between normal function calls and “Syntax (e.g.g for loops, variable declaration, etc., everything the parser has to handle in other programming languages) you can create your own syntax. Macros are in essence Manipulations of the Tree that constitutes your program.

I think Lisp has a KISS to programming. That you can manipulate syntax by using macros has lead to a phenomenon that Lisp has evolved quite dynamically. Need a new Language feature like – lets say object orientation – just write a OOP system with macros!

While C was extended roundabout 2 times with OOP features (C++, Obj. C) Lisp was extended multiple times, in the end there was a winner.

That is another quirk about Lisp, it evolves (see Clojure and Clojurescript for interesting new Mutation of lisp).

For its KISS properties Lisps is favored as teaching languages by some. Like Fogus outlines in his blueprint of an educational language (http://blog.fogus.me/2013/01/21/enfield-a-programming-language-designed-for-pedagogy/ )

To start, I’m strong believer that when learning new, and at times complex topics it’s outright detrimental to overload students with frivolous syntax rules. Therefore, Enfield is designed with minimal syntax rules and what’s more minimal than a Lisp like syntax.

1

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật