Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go -...

49
Writing Simple, Readable and Robust Code: Examples in Java, Clojure, and Go freiheit.com technologies - Hamburg, September 30, 2015 code.talks 2015

Transcript of Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go -...

Page 1: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Writing Simple, Readable and Robust Code: Examples in Java, Clojure, and Gofreiheit.com technologies - Hamburg, September 30, 2015code.talks 2015

Page 2: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

© freiheit.com 2

Hi! I am Stefan Richter. I started programming on an Apple ][ in 1983. Since 1998, I run my own software company, which is specialized in large Internet systems.WHO AM I?

Working as a professional programmer

Running my own company

20102005199819951990198719831966

Apple II,Pascal,C64,6502

VAX, Unix, C,4GLs,Smalltalk,Lisp,Scheme

Unix, C,4 GLs,(Windows NT, VB (!))

Linux, Java, Python, Ruby,Objective-C, C/C++, Common Lisp, Clojure, JavaScript, Go

...

Future

University studies and work as a programmer in research institutes

Page 3: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

SO LET’S GET STARTED AND HAVE SOME FUN! :)

Page 4: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

According to Wikipedia a domain- specific language (DSL) is a “... programming language or specification language dedicated to a particular problem domain [...].“

© freiheit.com 4

Page 5: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

I guess, we find DSLs so compelling because sooner or later every programmer dreams of creating his/her own programming language. ;)

© freiheit.com 5

Page 6: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Let’s take an example from a person you all know: Martin Fowler.

In 2005, he posted an article on his homepage about domain-specific languages ...

© freiheit.com 6

Page 7: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

© freiheit.com 7

Page 8: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Fowler creates a DSL to read files in a fixed-length format and to convert each line into an object. This is the file format and the mapping spec:

8© freiheit.com

Page 9: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

He uses C#, but as C# is more or less a Java clone you will agree that the Java code would look almost exactly the same.

© freiheit.com 9

Page 10: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Line by line, he reads the file and dispatches the conversion to a strategy class.

10© freiheit.com

Page 11: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

The strategy class is parameterized with the type code and the target class ...

11© freiheit.com

Page 12: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

The individual fields in each line are read by a FieldExtractor class ...

12© freiheit.com

Page 13: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

To process the line, the strategy creates the target class and uses the extractors to get the field data ...

13© freiheit.com

Page 14: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

The FieldExtractor pulls out the data and sets the correct field in the target class by using reflection ...

14© freiheit.com

Page 15: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Now you have an API that can be configured at compile time ...

15© freiheit.com

Page 16: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

If you would like to do this in a declarative style, you need an XML configuration ... (arrggh)

16© freiheit.com

Page 17: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Fowler’s example shows about 70 lines of code (LOC). But the code is not complete. I guess, the complete example would be around 200 LOC Java.

© freiheit.com 17

Page 18: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

HMM. SO MUCH CODE FOR A SIMPLE PROBLEM.

Page 19: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Ideally, you would like to have something like this as a mini DSL:

19© freiheit.com

Page 20: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

The Common Lisp Community picked up Fowler’s article, because he mentioned Lisp favorably.

© freiheit.com 20

Page 21: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Rainer Joswig then proposed this DSL in comp.lang.lisp:

21© freiheit.com

Page 22: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

His solution is a 12 LOC Lisp macro …,

22© freiheit.com

... which expands to this code:

Page 23: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Let’s examine how macros work. We’ll use the Common Lisp code as an example. It’s very close to how macros look like in Clojure! I’ll show you later! :)

© freiheit.com 23

Page 24: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

• Basically, a Lisp macro is a code generator. But you can’t compare Lisp macros with C macros or any other macro or template system.

• Lisp programs are trees of expressions. Code is data. Data is code.

• This is why a Lisp program can modify itself.

• In a nutshell - “How to read Lisp macros“:

• Macros don’t evaluate their parameters.

• The backtick (`) says: „Don’t evaluate the following code.“

• The comma (,) says: „Evaluate the following code and replace it with the result.“

• Then: Evaluate all resulting code together.

• Read Peter Seibel’s „Practical Common Lisp“ to learn more ...

24© freiheit.com

Page 25: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

• defclass creates a new CLOS class. In CLOS methods are defined independently of the class definition, but getter/setter can be defined implicitly.

• Methods are specialized on their parameters. So each method can be specialized to more than one class! Think about this for a moment ...

• You can even specialize a method depending on what exact value is passed as a parameter. So you can have special methods for specific parameter values. In this example, parse-line-for-class is called, when you pass a class with the name “service-call“. (Which in turn is generated by this macro, too).

• This method then takes the class, instantiates it and sets (side effect) its member variables by taking the format specification and using a substring function to extract the right bits and pieces from the input line. (This is not pure functional style, but this is how you use Common Lisp with CLOS.)

25© freiheit.com

Page 26: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

To read from a file and apply the mappings Rainer Joswig then wrote this code:

26© freiheit.com

Page 27: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Obviously, the Common Lisp version works almost the same as Martin Fowler’s C# version, with much less code. Rainer Joswig’s solution is functionally complete.Let’s do this in Clojure now!

© freiheit.com 27

Page 28: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

One thing: Some people in the Clojure community say that you should not use macros. You will soon see why. But I disagree! Macros are one of the reasons why you should favor Lisp-like languages over other languages.

© freiheit.com 28

Page 29: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

NOW THAT YOU ARE EXPERTS ON COMMON LISP MACRO, I WILL SHOW YOU THE FIRST CLOJURE VERSION STRAIGHT AWAY!

Page 30: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

30© freiheit.com

• We create a list of the field names because we need them two times in the code.

• defrecord creates a Clojure data structure with the look and feel of a hash table.

• But it is much more: Under the hood, this is a Java class! And even more!

• This is why you need a constructor function to create a “record“. I generate the name of the constructor function by appending “.“ to the record name.

• I create a multimethod, which is specialized on the value of the mapping parameter. So Clojure “knows“, which method to call based on the input ...

• Then, I create a list of all values from the input line (in the same order as their field names) and apply this list to the constructor of the record, which is returned as the function value.

Page 31: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

To make it complete: defmethod needs a defmulti to provide a dispatching function.

31© freiheit.com

• Meaning: Take the first four characters of the value of the parameter “line“ (which should be a java.lang.String) and then call the multimethod, which is specialized on this value.

• (Note: Of course, you have to handle the cases: What happens when you have an empty line, an unknown mapping or a comment line. But I leave this out here to keep the example simple, even though this is just five lines more including another defmethod, which handles all these :default cases).

Page 32: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

32© freiheit.com

• In a nutshell “How to read Clojure macros“:

• The backtick (`) says: „Don‘t evaluate the following code.“

• The tilde (~) says: „Evaluate the following code and replace it with the result.“

• ~@ means: “Evaluate the following code and replace it with the result, but don’t put parentheses around the result.“ (This is called „splicing“.)

• The hash sign (#) at the end of a variable is used to automatically create unique variable names in the code created by the macro. This is called auto-gensyms. This way you can prevent name clashes with call parameters.

Page 33: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Finally, this Clojure macro expands to the following code. Can you see the automatically generated variable names?

33© freiheit.com

Page 34: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Now we can define mappings as follows:

34© freiheit.com

Actually, the leading 0 doesn’t work here. Leading zeros are reserved for octal numbers.

Page 35: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

LET US BUILD SOMETHING TO CONVENIENTLY USE THESE MAPPINGS!

Page 36: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

This is a classic with-macro that you often find in the Common Lisp world.

36© freiheit.com

• It reads the file identified by file name line by line.

• It calls our multimethod parse line for each line.

• It binds the result to a variable that you can hand over to the macro (binding).

• And only if parsing the line has a result (no empty line, no comment line or unknown binding), it executes the body, meaning the code that you can hand over to this macro, which can contain the variable, which contains the binding.

• Then it returns a list of the results.

• Sounds difficult, but with the example on the next page you will find it to be very simple.

Page 37: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

In this example, we just extract the customer name from each line.

37© freiheit.com

• For each valid line in the file specified by file name (*testdata* contains the file name as a string) a Clojure record is created.

• Each result is bound in this case to the symbol/variable “obj“.

• In the body code you can use this symbol/variable to access this Clojure record.

• In this case, we use it to extract the customer name from the record.

• with-mappings then returns a list of the results.

Page 38: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Isn’t this awesome?! We have just defined two new programming constructs and added them to our programming language: defmapping, with-mappings.

© freiheit.com 38

Page 39: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

The complete example is about 30 LOC of Clojure. You don’t need any special tools (Language Workbench). It’s all right there in the Clojure language.And you just need Emacs to unleash this power ...

© freiheit.com 39

Page 40: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Now I can extend this without breaking the existing code.

© freiheit.com 40

Page 41: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Let’s add a serializer to create JSON from the data ...

© freiheit.com 41

Page 42: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

We define a protocol and add an implementation to the record definition.

42© freiheit.com

Page 43: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Now we can convert the data to JSON.

43© freiheit.com

And we can even change the serialize method „in place“ and on the fly.

Page 44: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

© freiheit.com GOTO Conference Copenhagen, 2011

“LISP ISN’T A LANGUAGE, IT’S A BUILDING MATERIAL.“

- ALAN KAY

Page 45: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

And Clojure gives you powerful abstractions that you won’t find in any other programming language. Some stuff is only possible in a Lisp-like language ... with all these parenthesis. ;)

© freiheit.com 45

Page 46: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

© freiheit.com GOTO Conference Copenhagen, 2011

IT IS COOL. I LOVE THIS. BUT IS THIS REALLY SIMPLE?LET’S DO THIS IN GO!

Page 47: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

© freiheit.com GOTO Conference Copenhagen, 2011

LIVE CODING

Page 48: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

THANK YOUfreiheit.com technologies

Budapester Str. 45

20359 Hamburg, Germany

[email protected]

T +49 40 890584 0

F +49 40 890584 20

Page 49: Stefan Richter - Writing simple, readable and robust code: Examples in Java, Clojure, and Go - code.talks 2015

Hackers wanted.

Join us:[email protected]