Simplicity is Complicated (2024)

dotGo

9 Nov 2015

Rob Pike

Google

Bonjour mes Gophers!

Simplicity is Complicated (1)

2

Success

What makes Go successful?

Many reasons cited:

  • speed of compilation
  • speed of execution
  • deployment
  • tools
  • libraries

These are not language features.
Less often cited: true language features such as interfaces or concurrency.

All are important but not really an answer.

3

Simplicity

My answer: Simplicity.

Go is simple, at least compared to established languages.

Simplicity has many facets.

Simplicity is complicated.

4

Convergence

Attended Lang.Next conference last year (2014).

Heard talks about new versions of Java, JavaScript (ECMAScript), Typescript, C#, C++, Hack (PHP), and more.

These languages actively borrow features from one another.

They are converging into a single huge language.

5

Language relativity

Sapir-Whorf hypothesis: Language influences thought.

Controversial with regard to human languages.

Close to a fact for computer languages. Consider:

  • logic programming
  • procedural programming
  • functional programming
  • object-oriented programming
  • concurrent programming

Like disciplines in mathematics.

6

Convergence and relativity

If the languages all converge, we will all think the same.

But different ways of thinking are good.

Need different languages for different problems.

We don't want just one tool, we want a set of tools, each best at one task.

7

Convergence and features

The talks at Lang.Next were about new language versions (Java 8, ECMAScript 6, C#, C++14) and were lists of features.

These languages evolve and compete by adding features.

The languages grow in complexity while becoming more similar.

Bloat without distinction.

8

Features in Go

Go is different.

Go does not try to be like the other languages.

Go does not compete on features.

As of Go 1, the language is fixed.

Many newcomers to Go ask for features from languages they know.
But those features do not belong in Go—and the language is fixed.

Adding features to Go would not make it better, just bigger.

That would make Go less interesting by being less different.

9

But you need features!

Of course, there must be some features.

But which ones? The right ones!

Design by consensus.

10

Readability

If a language has too many features, you waste time choosing which ones to use.
Then implement, refine, possibly rethink and redo.

Later, "Why does the code work this way?"

The code is harder to understand simply because it is using a more complex language.

Preferable to have just one way, or at least fewer, simpler ways.

Features add complexity. We want simplicity.

Features hurt readability. We want readability.

Readability is paramount.

11

Readable means Reliable

Readable code is reliable code.

It's easier to understand.

It's easier to work on.

If it breaks, it's easier to fix.

If the language is complicated:

  • You must understand more things to read and work on the code.
  • You must understand more things to debug and fix it.

A key tradeoff: More fun to write, or less work to maintain?

12

Expressiveness

Features are often suggested to aid "expressiveness".

Conciseness can be expressive but not always readable.
(Consider APL: ⊃ 1 ω ∨ . ∧ 3 4 = +/ +⌿ 1 0 ‾1 ∘.θ 1 - ‾1 Φ″ ⊂ ω)

Can also be expensive, implementing simple ideas with too-powerful primitives.
Performance can also be unpredictable.

On the other hand, verbosity can inhibit readability by obscuring the intent.

Build on, but do not be limited by, familiar ideas.

Be concise while remaining expressive.

13

The right set of features

Not features for features' sake.

Features that "cover the space", like a vector basis covering solution space.

Orthogonal features that interact predictably.

Simple features that interact in simple ways.

Simplicity comes from orthogonality and predictability.

Keep the language's goals in mind.

14

Go's goals

Clean procedural language designed for scalable cloud software.

Composable distinct elements, including:

  • concrete data types
  • functions and methods
  • interfaces
  • packages
  • concurrency

Plus: Good tools, fast builds.

All the pieces feel simple in practice.

15

Simple can be expressive

Simplicity is Complicated (2)

16

Don't need all those features

They add complexity without clarity.

Simplicity is Complicated (3)

17

Simplicity

Go is actually complex, but it feels simple.

Interacting elements must mesh seamlessly, without surprises.

Requires a lot of design, implementation work, refinement.

Simplicity is the art of hiding complexity.

18

A few simple things in Go

  • garbage collection
  • goroutines
  • constants
  • interfaces
  • packages

Each hides complexity behind a simple facade.

19

Garbage collection

Perhaps the best example of simplicity hiding complexity.

Very difficult to implement well, but worth it.
Yet so simple the language specification mentions it only in the introduction.
Nonetheless, parts of the language strongly influenced by it.

Code is simpler because GC is there.
Design does not need to include "ownership".

Go: there is no free. There is only garbage collection.
As simple as it can be. (But complex behind the facade.)

20

Concurrency

Concurrency is the ability to write your program as independently executing pieces.
In Go, concurrency has three elements:

  • goroutines (execution)
  • channels (communication)
  • select (coordination)

Let's talk about goroutines.

21

Goroutines

Start a goroutine with the go keyword:

go function(args)

Three extra keystrokes ('g', 'o', ' '). Hard to be much simpler.

Like garbage collection, eliminate considerations from the programmer's concern:

  • no stack size
  • no return or completion status
  • no mechanism for management
  • no "ID"

These are things other systems would provide. Go instead has a minimalist design.

Implementation complex, dependent on garbage collection for stack management.

22

Constants

In Go, constants are just numbers, even though (because) it is strongly typed.

var nanosecond = time.Second/1e9

Simple idea took about a year to work out. Difficulties:

  • "infinite" precision integers
  • "infinite" precision floating point (tried and failed with rationals)
  • promotion rules (i := 2; f := 2.0; g := 1/2; h := 1/2.0)
  • corner cases like shift
 fmt.Printf("%T %T", 2.0, 2.0<<0)

Still not totally satisfied, but the effect is that constants feel like numbers, contribute to the ease of using Go. But complicated behind the scenes.

More at this blog post.

23

Interfaces

Just a set of methods. No data. Simple idea, but more complex than expected.

type Reader interface { Read([]byte) (int, error)}

Also need variables of that type (var reader io.Reader).
These variables add dynamic typing to a statically typed language.

var r Reader = os.Stdin // Statically checked.var x interface{} = os.Stdin // Statically checked.r = x.(Reader) // Dynamically checked. Must be explicit here - design decision.

Requires careful design. Interface assignment must be implemented at run time (not a v-table). What if it fails? Led to type assertions and the "comma, ok" idiom.

More complexity crept in. Type assertions and type switches were not in the original plan.

24

Interfaces

Go's most distinctive and powerful feature.

Profound effect on library design.

Enables true component architectures. Prime examples are io.Reader and io.Writer, generalizations of the Unix pipe idea.

Feel simple, worth the complexity.

25

Packages

A design for structuring programs and libraries.

package big...import "math/big"

Took a long time to design. Enable componentization, scalability, sharing, data hiding and isolation, ...

Affect program design, syntax, naming, building, linking, testing, ...

Separation of package path ("math/big") from package name (big).
Enabled the go get mechanism.

Intricate to implement yet natural to use.

After garbage collection, perhaps the highest ratio of true complexity to apparent simplicity, and therefore of the power of simplicity to hide complexity.

26

A "simple" example

package mainimport ( "fmt" "log" "net/http")func こんにちは_Gophers(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "こんにちは Gophers!\n")}func main() { http.HandleFunc("/", こんにちは_Gophers) err := http.ListenAndServe("localhost:12345", nil) if err != nil { log.Fatal("ListenAndServe: ", err) }}
27

Hidden complexity

Unicode and UTF-8 handled seamlessly.

Packages imported and used easily.

Fprintf direct to network connection.

Function promoted to method (HandleFunc).

Truly concurrent—server won't block.

Production ready.

All very simple.

28

Summary

Simplicity is complicated but the clarity is worth the fight.

Simplicity is Complicated (4)

29

Conclusion

Simplicity is hard—to design.

Simplicity is complicated—to build.

But if you get it right...

Simplicity is easy—to use.

The success of Go proves it.

Simplicity is Complicated (5)

30

Thank you

dotGo

9 Nov 2015

Rob Pike

Google

https://go.dev/

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)

Simplicity is Complicated (2024)

References

Top Articles
Latest Posts
Article information

Author: Kelle Weber

Last Updated:

Views: 5858

Rating: 4.2 / 5 (53 voted)

Reviews: 84% of readers found this page helpful

Author information

Name: Kelle Weber

Birthday: 2000-08-05

Address: 6796 Juan Square, Markfort, MN 58988

Phone: +8215934114615

Job: Hospitality Director

Hobby: tabletop games, Foreign language learning, Leather crafting, Horseback riding, Swimming, Knapping, Handball

Introduction: My name is Kelle Weber, I am a magnificent, enchanting, fair, joyous, light, determined, joyous person who loves writing and wants to share my knowledge and understanding with you.