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

Post on 24-Jan-2017

771 views 1 download

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

Writing Simple, Readable and Robust Code: Examples in Java, Clojure, and Gofreiheit.com technologies - Hamburg, September 30, 2015code.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

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

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

© freiheit.com 4

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

© freiheit.com 5

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

© freiheit.com 7

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

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

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

10© freiheit.com

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

11© freiheit.com

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

12© freiheit.com

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

13© freiheit.com

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

14© freiheit.com

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

15© freiheit.com

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

16© freiheit.com

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

HMM. SO MUCH CODE FOR A SIMPLE PROBLEM.

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

19© freiheit.com

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

© freiheit.com 20

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

21© freiheit.com

His solution is a 12 LOC Lisp macro …,

22© freiheit.com

... which expands to this code:

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

• 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

• 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

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

26© freiheit.com

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

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

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

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.

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).

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.

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

33© freiheit.com

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.

LET US BUILD SOMETHING TO CONVENIENTLY USE THESE MAPPINGS!

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.

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.

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

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

Now I can extend this without breaking the existing code.

© freiheit.com 40

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

© freiheit.com 41

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

42© freiheit.com

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.

© freiheit.com GOTO Conference Copenhagen, 2011

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

- ALAN KAY

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

© freiheit.com GOTO Conference Copenhagen, 2011

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

© freiheit.com GOTO Conference Copenhagen, 2011

LIVE CODING

THANK YOUfreiheit.com technologies

Budapester Str. 45

20359 Hamburg, Germany

kontakt@freiheit.com

T +49 40 890584 0

F +49 40 890584 20

Hackers wanted.

Join us:jobs@freiheit.com