├── .gitignore ├── BACKUP.md ├── Makefile ├── README.md ├── basics ├── README.md ├── exceptions │ └── exeptions.go ├── flags │ └── main.go ├── hello │ └── main.go ├── index │ ├── index.go │ └── index_test.go ├── palindrome │ ├── palindrome.go │ └── palindrome_test.go ├── sorting │ └── main.go ├── swap │ └── main.go └── types │ ├── list │ └── lists.go │ ├── simpletypes.go │ ├── slices │ ├── slices.go │ └── slices_test.go │ └── strings │ ├── strings.go │ └── strings_test.go ├── cp ├── README.md ├── channels │ ├── blockingqueue │ │ ├── blockingqueue.go │ │ └── blockingqueue_test.go │ ├── fan │ │ ├── fanin.go │ │ ├── fanin_test.go │ │ ├── fanout.go │ │ └── fanout_test.go │ ├── philosophers │ │ ├── philosophers.go │ │ ├── philosophers_test.go │ │ └── table.go │ └── pingpong │ │ └── pingpong.go └── locks │ ├── README.md │ ├── blockingqueue │ ├── blockingqueue.go │ └── blockingqueue_test.go │ ├── philosophers │ ├── philosophers.go │ ├── philosophers_test.go │ └── table.go │ └── resourcemanager │ ├── README.md │ ├── resourcegraph.go │ ├── resourcegraph_test.go │ └── resourcemanager.go ├── docs ├── 1.0-About.pdf ├── 1.0-About.slide ├── 1.1-Overview.pdf ├── 1.1-Overview.slide ├── 1.2-Introduction to Golang.pdf ├── 1.2-Introduction to Golang.slide ├── 2.0-Go Programming - Basics and OOP.pdf ├── 2.0-Go Programming - Basics and OOP.slide ├── 3.0-Go-Programming-OOP.pdf ├── 3.0-Go-Programming-OOP.slide ├── 4.0-Go-Programming-Parser.pdf ├── 4.0-Go-Programming-Parser.slide ├── 5.0-Functional-Programming.pdf ├── 5.0-Functional-Programming.slide ├── 6.0-Concurrent-Programming.pdf ├── 6.0-Concurrent-Programming.slide ├── 7.0-Distributed-Programming-Raft.pdf ├── 7.0-Distributed-Programming-Raft.slide ├── 8.0-Systems-Programming.pdf ├── 8.0-Systems-Programming.slide ├── 9.0-Enterprise-Programming-Modules.pdf ├── 9.0-Enterprise-Programming-Modules.slide ├── README.md ├── exercises │ ├── Exercise1.md │ ├── Exercise2.1.md │ ├── Exercise2.2.md │ ├── Exercise3.md │ ├── Exercise4.md │ ├── Exercise5.md │ └── Exercise6.md ├── img │ ├── 00-programming-languages.jpeg │ ├── 01-cncf-projects.png │ ├── 01-exercise.png │ ├── 01-go-programming-language.png │ ├── 01-languages.png │ ├── 02-exercise.png │ ├── 03-exercise.key │ ├── 03-exercise.png │ ├── 04-lambda.key │ ├── 04-lambda.png │ ├── 04-parsers │ │ ├── 01-practice-go.gif │ │ ├── 02-menti.png │ │ ├── 02-parsers-for-what.png │ │ ├── 03-parsers-overview.png │ │ ├── 04-ast-vs-cst.png │ │ ├── 05-tree-terms.png │ │ ├── 05-whaat.gif │ │ ├── Parsers.006.png │ │ ├── Parsers.007.png │ │ ├── Parsers.008.png │ │ ├── Parsers.009.png │ │ ├── Parsers.010.png │ │ ├── Parsers.011.png │ │ └── programming.png │ ├── 05-alternative.svg │ ├── 05-ast.svg │ ├── 05-concatenation.svg │ ├── 05-input-is-text.svg │ ├── 05-number-grammar.svg │ ├── 05-once-or-more.svg │ ├── 05-optional.svg │ ├── 05-os-file.svg │ ├── 05-parser-is-function.svg │ ├── 05-repetition.svg │ ├── 05-result-is-anything.svg │ ├── 05-rune-array.svg │ ├── 05-strings-are-bytes.svg │ ├── 06-dining-philosophers.png │ ├── 06-go-concurrency.jpeg │ ├── 06-moores-law.png │ ├── 06-philosophers-channel.jpeg │ ├── 06-philosophers-channel.key │ ├── 07-idserv.png │ ├── 07-proxy-pattern.png │ ├── 09-conflicting-versions.png │ └── go.png ├── snippets │ └── cp ├── studywork │ ├── COP_Kotlin_Stefan_Bialek_.adoc │ ├── CoProgrammingLanguage.adoc │ ├── README.md │ ├── Study-Work-Presentation.pdf │ ├── Study-Work-Presentation.slide │ ├── StudyWork.xlsx │ ├── paper.md │ └── psta.md └── talks │ └── raft │ ├── Implementing Raft.pptx │ └── Implementing-Raft.slide ├── dp ├── idserv │ ├── Makefile │ ├── client │ │ └── main.go │ ├── core │ │ └── idserv-impl.go │ ├── idserv-api.go │ └── remote │ │ ├── idserv │ │ ├── idserv.pb.go │ │ └── idserv.proto │ │ ├── proxy │ │ └── proxy.go │ │ ├── server │ │ └── main.go │ │ └── stub │ │ └── idserv-stub.go └── kvstore │ ├── Makefile │ ├── README.md │ ├── core │ ├── kvstore-impl.go │ └── raft │ │ ├── cluster.go │ │ ├── cluster_test.go │ │ ├── configuration.go │ │ ├── node-utils.go │ │ ├── node.go │ │ ├── node_test.go │ │ ├── noderpc.go │ │ ├── replicatedlog.go │ │ ├── statemachine.go │ │ └── timer_test.go │ ├── kvstore-api.go │ └── remote │ ├── dkvs.go │ ├── proxy.go │ └── stub.go ├── fp ├── clojures │ └── main.go ├── composition │ └── main.go ├── lambdacalculus │ └── main.go ├── parser │ ├── boolparser.go │ ├── boolparser_test.go │ └── parser.go └── streams │ ├── streams.go │ └── streams_test.go ├── go.mod ├── go.sum ├── oop ├── boolparser │ ├── ast │ │ ├── ast.go │ │ └── ast_test.go │ ├── lexer │ │ ├── lexer.go │ │ └── lexer_test.go │ └── parser │ │ ├── parser.go │ │ └── parser_test.go ├── mail │ ├── client │ │ ├── client.go │ │ └── client_test.go │ ├── mail.go │ ├── smtp │ │ └── sender.go │ └── util │ │ └── registry.go ├── polymorphism │ ├── geoobject │ │ └── geoobject.go │ └── polymorphism.go ├── rational │ ├── rational.go │ └── rational_test.go └── stack │ ├── stack.go │ └── stack_test.go ├── raft └── raft.go └── sp ├── container.go ├── empty.go └── samples ├── os └── pid.go └── syscall ├── empty.go └── pid.go /.gitignore: -------------------------------------------------------------------------------- 1 | **/coverage.out 2 | docs/exercises/StudyWork.xlsx 3 | .idea/ 4 | *.iml 5 | .vscode/ 6 | **/.DS_Store 7 | 8 | 9 | -------------------------------------------------------------------------------- /BACKUP.md: -------------------------------------------------------------------------------- 1 | 2 | ## Lecture 2 - Basics in Go Programming, OOP Part I 3 | 4 | - Pointer, Arrays, Maps, Object Oriented Programming 5 | - Reasons for Go https://www.youtube.com/watch?v=5kj5ApnhPAE 6 | - Swap, Index, Rational Numbers, Containers 7 | 8 | ## Lecture 3 - Object Oriented Programming in Go 9 | 10 | - Structs, Interfaces, Embedding, Polymorphism 11 | 12 | ## Lecture 4 - Functional Programming and the Lambda Calculus 13 | - Functional Programming 14 | - Lambda Calculus 15 | - Streams in Go 16 | 17 | ## Lecture 5 - Functional Parsers 18 | - What is a Parser? 19 | - Functional Parsers and Parser Combinators 20 | - Building a Parser in Go 21 | 22 | ## Lecture 6 - Concurrent Programming 23 | - Why concurrent programming matters! 24 | - Go routines and channels 25 | - Go concurrency patterns 26 | - The dining philosophers problem 27 | 28 | ## Lecture 7 - Concurrent Programming - Part II 29 | - Deadlocks and Detection 30 | - The Resource Access Graph 31 | - Building a Resource Manager 32 | 33 | ## Lecture 8 - Distributed Programming 34 | - Introduction 35 | - Sockets and low level programming 36 | - RPC and GRPC 37 | - ID generator with GRPC 38 | 39 | ## Lecture 9 - Distributed Programming - The Raft Protocol 40 | - Introduction into Consensus Protocols 41 | - Raft 42 | - Implementing Raft with Go 43 | 44 | ## Lecture 10 - Modules and Versioning 45 | - Introduction into Go 1.11 Modules 46 | - Implementing Modules 47 | 48 | ## Lecture 11 - System Programming 49 | - What is System Programming? 50 | - Calling C 51 | - Implementing Docker 52 | 53 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Standard Makefile. Usage: make . Exclude raft package (slow tests!) 3 | # 4 | PKGS := $(shell go list ./... | grep -v /raft) 5 | 6 | .PHONY: test all build test install clean slideshow help coverage 7 | 8 | help: 9 | clear 10 | @echo "---------------------------------------------------------------------------------------------------------" 11 | @echo "Usage: make " 12 | @echo "---------------------------------------------------------------------------------------------------------" 13 | @echo "Valid targets are:" 14 | @echo " make all : Runs build, test, install." 15 | @echo " make build : Builds all packages." 16 | @echo " make test : Runs all tests." 17 | @echo " make install : Installs all packages." 18 | @echo " make clean : Clean up and clears caches." 19 | @echo " make coverage : Executes the tests with coverage and starts the go tool cover" 20 | @echo " make slideshow : Starts a golang present slideshow on port 3999. Blocks until CTRL-C ist pressed. " 21 | @echo " make help : This info. " 22 | @echo "---------------------------------------------------------------------------------------------------------" 23 | 24 | test: 25 | go test $(PKGS) 26 | 27 | build: 28 | go build $(PKGS) 29 | 30 | install: 31 | go install $(PKGS) 32 | 33 | clean: 34 | go clean -testcache -cache $(PKGS) 35 | rm *.out 36 | 37 | all: build test install 38 | 39 | coverage: 40 | go test -coverprofile=coverage.out $(PKGS) 41 | go tool cover -html=coverage.out 42 | 43 | slideshow: 44 | cd docs; present 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Concepts of Programming Languages 2 | Master Course: Concepts of Programming Languages - Rosenheim Technical University - 2019/2020 3 | 4 | 5 | 6 | The course "Concepts of Programming Languages" is designed as a master course. Solid programming skills in Java/C/C++ are required. I also assume that students have introductory skills in Scala and Python. For all exercises and examples, the course uses Go (Golang) as concrete example. 7 | We will look at typical styles and application areas like OOP, FP, Parallel-, Distributed- or Systems Programming. 8 | 9 | ## Goal of the Course 10 | 11 | 1. Learn the Concepts of Programming Languages 12 | 2. Learn how Go differs from Java, C/C++, Scala and languages 13 | 3. Get solid skills to pick the right language for a given problem 14 | 15 | It is somewhat unusual, that we focus on Go in all Lectures. Master students are typically well skilled in Java. They 16 | have some basic knowledge in C/C++, maybe JavaScript, Ruby, Python or Scala. Since Go is a multi paradigm language, the lecture uses Go to demonstrate the basic concepts of these languages and discuss features which are missing in Go. We go from concrete to the abstraction and not vice versa. 17 | Each student will compare Go to one of these languages as Semester Work: Modula, Ada, Smalltalk, C++, Eiffel, Objective C, Haskell, Clojure, F# , Erlang, Scala, D, Occam, Rust, Swift, JavaScript, Ruby, Python, Kotlin. We will release the results here. 18 | 19 | ## Lecture 1 - Introduction 20 | 21 | - Overview of Programming Languages, Why Go? 22 | - Setup, HelloWorld, Swap, Basic Types and Variables, Programm Arguments, Working with Go Flags, CLI Libraries 23 | - About 24 | - Overview 25 | - Introduction to Golang 26 | - Exercise 1 27 | 28 | ## Lecture 2 - Basics in Go Programming, OOP Part I 29 | 30 | - Pointer, Arrays, Maps and Object Oriented Programming 31 | - Reasons for Go https://www.youtube.com/watch?v=5kj5ApnhPAE 32 | - Slides 33 | - Exercise 2.1 34 | - Exercise 2.2 35 | 36 | ## Lecture 3 - Object Oriented Programming in Go, OOP Part II 37 | 38 | - Structs, Interfaces, Embedding, Polymorphism 39 | - Slides 40 | - Exercise 3.1 - Interfaces, Polymorphism and Embedding 41 | - Exercise 3.2 - Mail Component and Service Locator 42 | 43 | ## Lecture 4 - Building Parsers 44 | 45 | - Practicing Go using the example of parsers for boolean expressions 46 | - Slides 47 | - Exercise 4.1 - Lexer for boolean expressions 48 | - Exercise 4.2.1 - Abstract Syntax Tree (AST) 49 | - Exercise 4.2.2 Recursive Descent Parser 50 | - Exercise 4.3 Antlr 51 | 52 | ## Lecture 5 - Functional Programming 53 | - Introduction in Functional Programming, the Lambda Calculus and the implementation in Go 54 | - Slides 55 | - Exercise 5.1 - Warm Up with functional Programming in Go 56 | - Exercise 5.2 - Functional Composition 57 | - Exercise 5.3 - Map, Filter, Reduce with Streams 58 | - Exercise 5.4 - Functional Word Count 59 | 60 | ## Lecture 6 - Concurrent Programming 61 | - Why concurrent programming matters! Go routines and channels, Go concurrency patterns, The dining philosophers problem 62 | - Slides 63 | - Exercise 6 64 | 65 | ## Lecture 7 - Distributed Programming 66 | - Introduction into Consensus Protocols 67 | - Raft 68 | - Implementing Raft with Go 69 | - Slides 70 | 71 | -------------------------------------------------------------------------------- /basics/README.md: -------------------------------------------------------------------------------- 1 | # Basics 2 | 3 | - Exceptions - Java like exception handling based on panic, defer and recover 4 | - Flags for command line applications 5 | - Hello World (UTF-8) 6 | - Inverted index with maps and strings (book index example) 7 | - Palindrome with strings and runes (UTF-8 safe) 8 | - Sorting of custom types 9 | - Swap with Pointers 10 | - Types: Primitive types, maps, strings and slices 11 | -------------------------------------------------------------------------------- /basics/exceptions/exeptions.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // TryCatchBlock is a codeblock with a try and optional catch, finally clause. 8 | type TryCatchBlock struct { 9 | T func() 10 | C func(Exception) 11 | F func() 12 | } 13 | 14 | // Exception is a Exception type. 15 | type Exception interface{} 16 | 17 | // Throw is a alias for panic. 18 | func Throw(up Exception) { 19 | panic(up) 20 | } 21 | 22 | // Do does call the try function als installs a catch and finally handler. 23 | func (tcf TryCatchBlock) Do() { 24 | if tcf.F != nil { 25 | defer tcf.F() 26 | } 27 | if tcf.C != nil { 28 | defer func() { 29 | if r := recover(); r != nil { 30 | tcf.C(r) 31 | } 32 | }() 33 | } 34 | tcf.T() 35 | } 36 | 37 | // TryCatch helper for a better syntax. 38 | func TryCatch(t func(), c func(ex Exception), f func()) { 39 | TryCatchBlock{T: t, C: c, F: f}.Do() 40 | } 41 | 42 | // Test exception handling. 43 | func main() { 44 | 45 | fmt.Println("Starting ...") 46 | 47 | TryCatch( 48 | func() { 49 | fmt.Println("Trying ...") 50 | Throw("Some Exception") // throws an exception 51 | }, 52 | func(e Exception) { 53 | fmt.Printf("Caught %v\n", e) 54 | }, 55 | func() { 56 | fmt.Println("Finally...") 57 | }) 58 | fmt.Println("Shutdown gracefully") 59 | } 60 | -------------------------------------------------------------------------------- /basics/flags/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | package main 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | ) 10 | 11 | // Simple test for the Go flag API. 12 | func main() { 13 | // construct a string flag with a default ip address and a description. 14 | ip := flag.String("ip", "192.168.1.1", "Overrides the default IP address.") 15 | port := flag.String("port", "8080", "Overrides the default listen port.") 16 | 17 | // flag.Args() parses the arg of our program. 18 | if len(flag.Args()) == 0 { 19 | fmt.Printf("Program Usage:\n") 20 | // PrintDefaults() prints a description and the default values to stdout. 21 | flag.PrintDefaults() 22 | } 23 | 24 | flag.Parse() 25 | 26 | fmt.Println("\nDefault value for IP: " + *ip) 27 | fmt.Println("\nDefault value for port: " + *port) 28 | } 29 | 30 | // END OMIT 31 | -------------------------------------------------------------------------------- /basics/hello/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | package main 5 | 6 | import "fmt" 7 | 8 | func main() { 9 | fmt.Printf("Hello %s", "Programming with Go \xE2\x98\xAF\n") // \xE2\x98\xAF -> ☯ 10 | fmt.Printf("Hello %s", "Programming with Go ☯\n") 11 | } 12 | -------------------------------------------------------------------------------- /basics/index/index.go: -------------------------------------------------------------------------------- 1 | // Package index makes a book index out of pages. 2 | // Copyright 2018 Johannes Weigend, Johannes Siedersleben 3 | // Licensed under the Apache License, Version 2.0 4 | package index 5 | 6 | import "fmt" 7 | 8 | // Page contains an array of words. 9 | type Page []string 10 | 11 | // Book is an array of pages. 12 | type Book []Page 13 | 14 | // Index contains a list of pages for each word in a book. 15 | type Index map[string][]int 16 | 17 | // MakeIndex generates an index structure 18 | func MakeIndex(book Book) Index { 19 | idx := make(Index) 20 | for i, page := range book { 21 | for _, word := range page { 22 | pages := idx[word] 23 | idx[word] = append(pages, i) 24 | } 25 | } 26 | return idx 27 | } 28 | 29 | // Stringer support 30 | func (idx Index) String() string { 31 | result := "" 32 | for k, v := range idx { 33 | result += fmt.Sprintf("\n\tWord: %v : Pages: %v", k, v) 34 | } 35 | return result + "\n" 36 | } 37 | 38 | // MakePage constructs a page from a string array. 39 | func MakePage(words []string) Page { 40 | page := new(Page) 41 | *page = words 42 | return *page 43 | } 44 | 45 | // MakeBook constructs a book from a page array. 46 | func MakeBook(pages []Page) Book { 47 | book := new(Book) 48 | *book = pages 49 | return *book 50 | } 51 | -------------------------------------------------------------------------------- /basics/index/index_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend, Johannes Siedersleben 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | package index 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | ) 10 | 11 | func TestIndex(t *testing.T) { 12 | 13 | // prepare book 14 | p1 := MakePage([]string{"A", "A", "B", "C"}) 15 | p2 := MakePage([]string{"A", "C", "D"}) 16 | p3 := MakePage([]string{"A", "B", "D"}) 17 | book := MakeBook([]Page{p1, p2, p3}) 18 | 19 | // calculate index 20 | idx := MakeIndex(book) 21 | 22 | // stringer support => not automated 23 | fmt.Printf("Index: %v", idx) 24 | } 25 | -------------------------------------------------------------------------------- /basics/palindrome/palindrome.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend, Johannes Siedersleben 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package palindrome implements multiple functions for palindromes. 5 | package palindrome 6 | 7 | import "github.com/jweigend/concepts-of-programming-languages/basics/types/strings" 8 | 9 | // IsPalindrome implementation. Does only work for 1-Byte UTF-8 chars (ASCII). 10 | func IsPalindrome(word string) bool { 11 | for pos := 0; pos < len(word)/2; pos++ { 12 | if word[pos] != word[len(word)-pos-1] { 13 | return false 14 | } 15 | } 16 | return true 17 | } 18 | 19 | // END1 OMIT 20 | 21 | // IsPalindrome2 is using runes. This works for all UTF-8 chars (SBC, MBC). 22 | func IsPalindrome2(word string) bool { 23 | var runes = []rune(word) 24 | for pos, ch := range runes { 25 | if ch != runes[len(runes)-pos-1] { 26 | return false 27 | } 28 | } 29 | return true 30 | } 31 | 32 | // END2 OMIT 33 | 34 | // IsPalindrome3 is implemented by reusing Reverse(). Reverse works for UTF-8 chars. 35 | func IsPalindrome3(word string) bool { 36 | return strings.Reverse(word) == word 37 | } 38 | 39 | // END3 OMIT 40 | -------------------------------------------------------------------------------- /basics/palindrome/palindrome_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend, Johannes Siedersleben 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | package palindrome 5 | 6 | import "testing" 7 | 8 | //START OMIT 9 | // palindrome_test.go 10 | func TestPalindrome(t *testing.T) { 11 | if !IsPalindrome("") { 12 | t.Error("isPalindrome('' should be true. But is false.") 13 | } 14 | if !IsPalindrome("o") { 15 | t.Error("isPalindrome('o' should be true. But is false.") 16 | } 17 | if !IsPalindrome("oto") { 18 | t.Error("isPalindrome('oto' should be true. But is false.") 19 | } 20 | if IsPalindrome("ottos") { 21 | t.Error("isPalindrome('ottos' should be false. But is true.") 22 | } 23 | //END OMIT 24 | } 25 | 26 | func TestPalindrome2(t *testing.T) { 27 | testPalindromeUTF8(t, IsPalindrome2) 28 | } 29 | 30 | func TestPalindrome3(t *testing.T) { 31 | testPalindromeUTF8(t, IsPalindrome3) 32 | } 33 | 34 | func testPalindromeUTF8(t *testing.T, isPalindrome func(word string) bool) { 35 | if !isPalindrome("☯otto☯") { 36 | t.Error("isPalindrome('☯otto☯' should be true. But is false.") 37 | } 38 | if !isPalindrome("") { 39 | t.Error("isPalindrome(Empty string should be a palindrome. But is not. Method returns false.") 40 | } 41 | if !isPalindrome("o") { 42 | t.Error("isPalindrome('o' should be true. But is false.") 43 | } 44 | if !isPalindrome("oto") { 45 | t.Error("isPalindrome('oto' should be true. But is false.") 46 | } 47 | if isPalindrome("ottos") { 48 | t.Error("isPalindrome('ottos' should be false. But is true.") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /basics/sorting/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "sort" 6 | ) 7 | 8 | // AxisSorter sorts planets by axis. 9 | type AxisSorter []Planet 10 | 11 | func (a AxisSorter) Len() int { return len(a) } 12 | func (a AxisSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 13 | func (a AxisSorter) Less(i, j int) bool { return a[i].Axis < a[j].Axis } 14 | 15 | // NameSorter sorts planets by name. 16 | type NameSorter []Planet 17 | 18 | func (a NameSorter) Len() int { return len(a) } 19 | func (a NameSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 20 | func (a NameSorter) Less(i, j int) bool { return a[i].Name < a[j].Name } 21 | 22 | // Planet represents a planet in our solarsystem. 23 | type Planet struct { 24 | Name string `json:"name"` 25 | Aphelion float64 `json:"aphelion"` // in million km 26 | Perihelion float64 `json:"perihelion"` // in million km 27 | Axis int64 `json:"axis"` // in km 28 | Radius float64 `json:"radius"` 29 | } 30 | 31 | func main() { 32 | var mars Planet 33 | mars.Name = "Mars" 34 | mars.Aphelion = 249.2 35 | mars.Perihelion = 206.7 36 | mars.Axis = 227939100 37 | mars.Radius = 3389.5 38 | 39 | var earth Planet 40 | earth.Name = "Earth" 41 | earth.Aphelion = 151.930 42 | earth.Perihelion = 147.095 43 | earth.Axis = 149598261 44 | earth.Radius = 6371.0 45 | 46 | var venus Planet 47 | venus.Name = "Venus" 48 | venus.Aphelion = 108.939 49 | venus.Perihelion = 107.477 50 | venus.Axis = 108208000 51 | venus.Radius = 6051.8 52 | 53 | planets := []Planet{mars, venus, earth} 54 | log.Println("unsorted:", planets) 55 | 56 | sort.Sort(AxisSorter(planets)) 57 | log.Println("by axis:", planets) 58 | 59 | sort.Sort(NameSorter(planets)) 60 | log.Println("by name:", planets) 61 | } 62 | -------------------------------------------------------------------------------- /basics/swap/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend, Johannes Siedersleben 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | package main 5 | 6 | import "fmt" 7 | 8 | func main() { 9 | var a, b = 1, 2 10 | fmt.Printf("Initial : a=%d, b=%d\n", a, b) 11 | a, b = b, a 12 | fmt.Printf("After a,b = b,a : a=%d, b=%d\n", a, b) 13 | swap0 := func(x, y int) (int, int) { 14 | return y, x 15 | } 16 | a, b = swap0(a, b) 17 | fmt.Printf("After a,b = swap0(a,b) : a=%d, b=%d\n", a, b) 18 | swap1(a, b) 19 | fmt.Printf("After swap1(a,b) : a=%d, b=%d\n", a, b) 20 | swap2(&a, &b) 21 | fmt.Printf("After swap2(&a,&b) : a=%d, b=%d\n", a, b) 22 | pa, pb := &a, &b 23 | swap3(&pa, &pb) 24 | fmt.Printf("After swap3(&pa, &pb): a=%d, b=%d, pa=%p, pb, %p\n", a, b, pa, pb) 25 | } 26 | 27 | // END0 OMIT 28 | 29 | func swap1(x, y int) { 30 | x, y = y, x 31 | } 32 | 33 | // END1 OMIT 34 | 35 | func swap2(x *int, y *int) { 36 | *x, *y = *y, *x 37 | } 38 | 39 | // END2 OMIT 40 | 41 | func swap3(x **int, y **int) { 42 | *x, *y = *y, *x 43 | } 44 | 45 | // END3 OMIT 46 | -------------------------------------------------------------------------------- /basics/types/list/lists.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 by Johannes Siedersleben 2 | // 04.07.2018 3 | 4 | package main 5 | 6 | import ( 7 | "container/list" 8 | "fmt" 9 | ) 10 | 11 | func main() { 12 | var xs = list.New() 13 | for i := 0; i < 10; i++ { 14 | xs.PushBack(i+1000) 15 | } 16 | 17 | var x list.Element 18 | x.Value = 7 19 | 20 | var y *list.Element 21 | y = xs.Front() 22 | fmt.Println(y.Value) 23 | 24 | var sum int 25 | for x := xs.Front(); x != nil; x = x.Next() { 26 | sum += x.Value.(int) 27 | } 28 | 29 | fmt.Print(sum) 30 | } 31 | -------------------------------------------------------------------------------- /basics/types/simpletypes.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 by Johannes Siedersleben 2 | // 29.06.2018 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "math" 9 | ) 10 | 11 | func nop(x int) {} // no effect 12 | func add(x, y int) int { return x + y } 13 | func addp(px, py *int) int { return *px + *py } // adds what px, py are pointing to 14 | func swap(px, py *int) { *px, *py = *py, *px } // swaps what px, py are pointing to 15 | func swapp(ppx, ppy **int) { *ppx, *ppy = *ppy, *ppx } // swaps what ppx, ppy are pointing to 16 | 17 | // Integer is an alias for int. 18 | type Integer = int 19 | 20 | // Int is struct containing an int value. 21 | type Int struct { 22 | value Integer 23 | } 24 | 25 | var m = 400 26 | 27 | // Automatic increment per line with iota 28 | const ( 29 | zero = iota 30 | one 31 | two 32 | ) 33 | 34 | func id(x *int) *int { return x } 35 | func add500() { m += 500 } 36 | func inc(x *int) { *x++ } 37 | 38 | func (x Int) Add(y Int) Int { return NewInt(x.value + y.value) } 39 | func NewInt(value int) Int { var v = new(Int); v.value = value; return *v } 40 | func Add(x, y Integer) Integer { return x + y } 41 | func Exec(f func()) { f() } 42 | func Execl(f func(int), arg int) { f(arg) } 43 | 44 | func main() { 45 | var c = nop 46 | var a = 6 47 | 48 | const z complex64 = complex(100, 200) 49 | const pi = math.Pi 50 | fmt.Println(real(z), imag(z), pi) 51 | 52 | c(a) // calls nop 53 | Exec(add500) // calls add500, m = 900 54 | fmt.Println(m) 55 | Execl(func(x int) { fmt.Println(x) }, 7777) // prints 7777 56 | 57 | inc(&a) 58 | fmt.Println(a) // 7 59 | a = 6 60 | b := 8 61 | b = 9 62 | fmt.Println(a, b) // 6, 9 63 | swap(&a, &b) // &a, &b unchanged, a, b swapped 64 | fmt.Println(a, b) // 9, 6 65 | 66 | var q = id(&a) 67 | fmt.Println(*q) // *q = 9 68 | 69 | var r = NewInt(78) 70 | var s = NewInt(43) 71 | var t = r.Add(s) 72 | fmt.Println(t) // {121} 73 | 74 | var u = 29 75 | var v = 67 76 | var w = Add(u, v) 77 | fmt.Println(w) 78 | 79 | pa := &a // *pa == a 80 | pb := &b // *pb == b 81 | swap(pa, pb) // a, b swapped 82 | fmt.Println(a, b) // 6, 9 83 | 84 | swap(&a, &b) // a, b swapped 85 | fmt.Println(*pa, *pb) // 9, 6 86 | 87 | ppa := &pa 88 | ppb := &pb 89 | swapp(ppa, ppb) // pa, pb swapped, a, b untouched 90 | fmt.Println(*pa, *pb) // 6, 9 91 | fmt.Println(a, b) // 9, 6 92 | 93 | fmt.Println(add(a, b)) 94 | fmt.Println(addp(&a, &b)) 95 | 96 | xs := []int{11, 22, 33} 97 | var sum int 98 | for _, x := range xs { 99 | sum += x 100 | } 101 | fmt.Println(sum) 102 | } 103 | -------------------------------------------------------------------------------- /basics/types/slices/slices.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | package slices 4 | -------------------------------------------------------------------------------- /basics/types/slices/slices_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | package slices 4 | 5 | import ( 6 | "fmt" 7 | "testing" 8 | ) 9 | 10 | func TestSliceAppend(t *testing.T) { 11 | array := [2]string{"A", "B"} 12 | slice := array[:] // slice the array 13 | fmt.Printf("Slice: %v, Capacity: %v, Length: %v\n", slice, cap(slice), len(slice)) 14 | 15 | // append() does not work for arrays, only for slices. 16 | slice = append(slice, "C") 17 | fmt.Printf("Slice: %v, Capacity: %v, Length: %v\n", slice, cap(slice), len(slice)) 18 | slice = append(slice, "D") 19 | slice = append(slice, "E") 20 | fmt.Printf("Slice: %v, Capacity: %v, Length: %v\n", slice, cap(slice), len(slice)) 21 | } 22 | 23 | func TestSliceCopy(t *testing.T) { 24 | array := [2]string{"A", "B"} 25 | slice := array[:1] // slice the array 26 | fmt.Printf("Array: %p\n", &array) 27 | fmt.Printf("Slice: %p\n", slice) 28 | slice1 := slice 29 | fmt.Printf("Slice 1: %p\n", slice1) 30 | 31 | f := func(s []string) { 32 | fmt.Printf("Slice 1: %p\n", s) 33 | } 34 | f(slice1) 35 | 36 | slice1 = append(slice1, "C") 37 | fmt.Printf("Slice 1: %p\n", slice1) 38 | 39 | array[0] = "X" 40 | fmt.Printf("Slice: %s, %p\n", slice, slice) 41 | fmt.Printf("Array: %s, %p\n", array, &array) 42 | 43 | } 44 | -------------------------------------------------------------------------------- /basics/types/strings/strings.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package strings contains string utility functions. 5 | package strings 6 | 7 | // Reverse reverses an unicode string. 8 | func Reverse(s string) string { 9 | r := []rune(s) 10 | for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { 11 | r[i], r[j] = r[j], r[i] 12 | } 13 | return string(r) 14 | } 15 | 16 | // End OMIT 17 | -------------------------------------------------------------------------------- /basics/types/strings/strings_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | package strings 4 | 5 | import ( 6 | "fmt" 7 | "testing" 8 | ) 9 | 10 | func TestStringLength(t *testing.T) { 11 | 12 | // Unicode code length 13 | length := len("A") 14 | fmt.Printf("Length: %d\n", length) 15 | length = len("±") 16 | fmt.Printf("Length: %d\n", length) 17 | length = len("☯") 18 | fmt.Printf("Length: %d\n", length) 19 | } 20 | 21 | func TestReverse(t *testing.T) { 22 | 23 | s1 := "Hello, world" 24 | s2 := "Hello, 世界" 25 | s3 := "The quick bròwn 狐 jumped over the lazy 犬" 26 | 27 | if Reverse(Reverse(s1)) != s1 { 28 | t.Errorf("Reversed string %s ist not equal to %s", Reverse(Reverse(s1)), s1) 29 | } 30 | if Reverse(Reverse(s2)) != s2 { 31 | t.Errorf("Reversed string %s ist not equal to %s", Reverse(Reverse(s2)), s2) 32 | } 33 | if Reverse(Reverse(s3)) != s3 { 34 | t.Errorf("Reversed string %s ist not equal to %s", Reverse(Reverse(s3)), s3) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /cp/README.md: -------------------------------------------------------------------------------- 1 | # Concurrent Programming Samples 2 | 3 | - channels 4 | - locks 5 | 6 | 7 | ## Channels 8 | 9 | ## Locks 10 | 11 | -------------------------------------------------------------------------------- /cp/channels/blockingqueue/blockingqueue.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package blockingqueue contains a blocking LIFO container. 5 | package blockingqueue 6 | 7 | // BlockingQueue is a FIFO container with a fixed capacity. 8 | // It blocks a reader when it is empty and a writer when it is full. 9 | type BlockingQueue struct { 10 | channel chan interface{} 11 | } 12 | 13 | // NewBlockingQueue constructs a BlockingQueue with a given capacity. 14 | func NewBlockingQueue(capacity int) *BlockingQueue { 15 | q := BlockingQueue{make(chan interface{}, capacity)} 16 | return &q 17 | } 18 | 19 | // Put puts an item in the queue and blocks it the queue is full. 20 | func (q *BlockingQueue) Put(item interface{}) { 21 | q.channel <- item 22 | } 23 | 24 | // Take takes an item from the queue and blocks if the queue is empty. 25 | func (q *BlockingQueue) Take() interface{} { 26 | return <-q.channel 27 | } 28 | 29 | // EOF 30 | -------------------------------------------------------------------------------- /cp/channels/blockingqueue/blockingqueue_test.go: -------------------------------------------------------------------------------- 1 | package blockingqueue 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | // Copyright 2018 Johannes Weigend 10 | // Licensed under the Apache License, Version 2.0 11 | 12 | func TestBlockingQueue(t *testing.T) { 13 | bq1 := NewBlockingQueue(1) 14 | done := make(chan bool) 15 | // slow writer 16 | go func(bq *BlockingQueue) { 17 | bq.Put("A") 18 | time.Sleep(100 * time.Millisecond) 19 | bq.Put("B") 20 | time.Sleep(100 * time.Millisecond) 21 | bq.Put("C") 22 | }(bq1) 23 | // reader will be blocked 24 | go func(bq *BlockingQueue) { 25 | item := bq.Take() 26 | fmt.Printf("Got %v\n", item) 27 | item = bq.Take() 28 | fmt.Printf("Got %v\n", item) 29 | item = bq.Take() 30 | fmt.Printf("Got %v\n", item) 31 | done <- true 32 | }(bq1) 33 | 34 | <-done 35 | } 36 | 37 | // EOF OMIT 38 | -------------------------------------------------------------------------------- /cp/channels/fan/fanin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package utils contains functions to work with channels. 5 | package utils 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | ) 11 | 12 | // FanIn reads from N-Channels and forwards the result to the output channel. 13 | func FanIn(channels []chan int, output chan int) { 14 | for i := 0; i < len(channels); i++ { 15 | // fan in 16 | go func(i int) { 17 | for { 18 | n, ok := <-channels[i] 19 | if !ok { 20 | break 21 | } 22 | output <- n 23 | } 24 | log.Println("input channel closed: done.") 25 | }(i) 26 | } 27 | } 28 | 29 | // FanIn OMIT 30 | 31 | // Print prints all data from a channel until the channel is closed. 32 | func Print(ch chan int) { 33 | go func() { 34 | for { 35 | res, ok := <-ch 36 | if !ok { 37 | break 38 | } 39 | fmt.Println(res) 40 | } 41 | log.Println("output channel closed: done.") 42 | }() 43 | } 44 | -------------------------------------------------------------------------------- /cp/channels/fan/fanin_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package fan contains examples for fanout und fanin functions using channels. 5 | package utils 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func TestFanIn(t *testing.T) { 13 | 14 | // FanIn reads from an array of input channels and send the results to a output channel . 15 | inCh := []chan int{make(chan int), make(chan int)} 16 | outCh := make(chan int) 17 | 18 | // FanIn from an array of channels 19 | FanIn(inCh, outCh) 20 | 21 | // read from one channel and print results to stdout. 22 | Print(outCh) 23 | 24 | inCh[0] <- 2 25 | inCh[1] <- 1 26 | 27 | time.Sleep(100 * time.Millisecond) 28 | 29 | close(inCh[0]) 30 | close(inCh[1]) 31 | 32 | time.Sleep(100 * time.Millisecond) 33 | } 34 | -------------------------------------------------------------------------------- /cp/channels/fan/fanout.go: -------------------------------------------------------------------------------- 1 | // Package utils contains examples for fanout und fanin functions using channels. 2 | package utils 3 | 4 | // FanOut reads from a channel and starts an async processing task. 5 | // The result values of the tasks will be returned in the result channel 6 | func FanOut(input chan int, task func(int, chan int)) chan int { 7 | result := make(chan int) 8 | go func() { 9 | for { 10 | x, ok := <-input 11 | if !ok { 12 | break 13 | } 14 | go task(x, result) 15 | } 16 | }() 17 | return result 18 | } 19 | 20 | // EOF OMIT 21 | -------------------------------------------------------------------------------- /cp/channels/fan/fanout_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package fan contains examples for fanout und fanin functions using channels. 5 | package utils 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func TestFanOut(t *testing.T) { 13 | 14 | inCh := make(chan int, 10) 15 | // write data to in channel 16 | for i := 0; i < 10; i++ { 17 | inCh <- i 18 | } 19 | 20 | task := func(v int, res chan int) { 21 | time.Sleep(1 * time.Millisecond) // simulate long running calculation 22 | res <- v * v // return v*v 23 | } 24 | 25 | outChan := FanOut(inCh, task) 26 | 27 | Print(outChan) 28 | 29 | time.Sleep(100 * time.Millisecond) 30 | 31 | close(inCh) 32 | } 33 | -------------------------------------------------------------------------------- /cp/channels/philosophers/philosophers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package philosophers 16 | 17 | import ( 18 | "fmt" 19 | "time" 20 | ) 21 | 22 | // Philosopher represents one philosopher. 23 | type Philosopher struct { 24 | id int 25 | table *Table 26 | } 27 | 28 | // NewPhilosopher constructs a philosopher. 29 | func NewPhilosopher(id int, table *Table) *Philosopher { 30 | p := new(Philosopher) 31 | p.id = id 32 | p.table = table 33 | return p 34 | } 35 | 36 | // Run loops forever. 37 | func (p *Philosopher) run() { 38 | for { 39 | p.takeForks() 40 | p.eat() 41 | p.putForks() 42 | p.think() 43 | } 44 | } 45 | 46 | // Take forks by channeling our id to the table and wait until the table returns true on the reserved channel. 47 | func (p *Philosopher) takeForks() { 48 | // try to get forks from table 49 | gotForks := false 50 | for !gotForks { 51 | p.table.getTakeChannel() <- p.id 52 | gotForks = <-p.table.getReservedChannel(p.id) 53 | } 54 | } 55 | 56 | // Put forks by channeling our id to the table. The table is responsible for the put logic. 57 | func (p *Philosopher) putForks() { 58 | p.table.getPutChannel() <- p.id 59 | } 60 | 61 | // Eating. 62 | func (p *Philosopher) eat() { 63 | fmt.Printf("[->]: Philosopher #%d eats ...\n", p.id) 64 | time.Sleep(2 * time.Millisecond) 65 | fmt.Printf("[<-]: Philosopher #%d eat ends.\n", p.id) 66 | } 67 | 68 | // Thinking. 69 | func (p *Philosopher) think() { 70 | fmt.Printf("[->]: Philosopher #%d thinks ...\n", p.id) 71 | time.Sleep(3 * time.Millisecond) 72 | fmt.Printf("[<-]: Philosopher #%d thinking ends\n", p.id) 73 | } 74 | -------------------------------------------------------------------------------- /cp/channels/philosophers/philosophers_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package philosophers 15 | 16 | import ( 17 | "testing" 18 | "time" 19 | ) 20 | 21 | // ===================================================================================================================== 22 | // Main Test. 23 | // ===================================================================================================================== 24 | 25 | func TestPhilosophers(t *testing.T) { 26 | 27 | var COUNT = 5 28 | 29 | // start table for 5 30 | table := NewTable(COUNT) 31 | 32 | // start philosophers 33 | for i := 0; i < COUNT; i++ { 34 | philosopher := NewPhilosopher(i, table) 35 | go philosopher.run() 36 | } 37 | go table.run() 38 | 39 | // wait 1 millisecond --> check output 40 | time.Sleep(10 * time.Millisecond) 41 | } 42 | -------------------------------------------------------------------------------- /cp/channels/philosophers/table.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package philosophers implements the Dining Philosophers Problem. 16 | package philosophers 17 | 18 | // ===================================================================================================================== 19 | // Dining Philosophers Problem. See https://en.wikipedia.org/wiki/Dining_philosophers_problem. 20 | // The synchronization is done with channels. There are two channels for put and request fork operation. 21 | // There is one channel per philosopher to signal when the forks can be taken. 22 | // ===================================================================================================================== 23 | 24 | // Table represents the table with dynamic seat count. 25 | type Table struct { 26 | // channel for take requests - answer is sent over the reservedCh. 27 | takeCh chan int 28 | // channel to put fork requests. 29 | putCh chan int 30 | // direct communication between philosopher and table for getting fork response. 31 | reservedCh []chan bool 32 | forkInUse []bool 33 | nbrOfSeats int 34 | } 35 | 36 | // NewTable constructs a table with n seats. 37 | func NewTable(nbrOfSeats int) *Table { 38 | table := new(Table) 39 | 40 | // initialize channels 41 | table.takeCh = make(chan int) 42 | table.putCh = make(chan int) 43 | 44 | table.reservedCh = make([]chan bool, nbrOfSeats) 45 | for i := 0; i < nbrOfSeats; i++ { 46 | table.reservedCh[i] = make(chan bool) 47 | } 48 | table.forkInUse = make([]bool, nbrOfSeats) 49 | table.nbrOfSeats = nbrOfSeats 50 | return table 51 | } 52 | 53 | // Function run() contains the main loop for assigning forks and starting philosophers. 54 | func (t *Table) run() { 55 | 56 | for { 57 | select { 58 | case requestedFork := <-t.takeCh: 59 | { 60 | if !t.forkInUse[requestedFork] && !t.forkInUse[(requestedFork+1)%t.nbrOfSeats] { 61 | // both forks are not in use -> reserve 62 | t.forkInUse[requestedFork] = true 63 | t.forkInUse[(requestedFork+1)%t.nbrOfSeats] = true 64 | t.reservedCh[requestedFork] <- true 65 | } else { 66 | t.reservedCh[requestedFork] <- false // not valid try again --> see loop in takeForks. 67 | } 68 | } 69 | case putFork := <-t.putCh: 70 | { 71 | // put forks 72 | t.forkInUse[putFork] = false 73 | t.forkInUse[(putFork+1)%t.nbrOfSeats] = false 74 | } 75 | } 76 | } 77 | } 78 | 79 | func (t *Table) getTakeChannel() chan int { 80 | return t.takeCh 81 | } 82 | 83 | func (t *Table) getPutChannel() chan int { 84 | return t.putCh 85 | } 86 | 87 | func (t *Table) getReservedChannel(id int) chan bool { 88 | return t.reservedCh[id] 89 | } 90 | -------------------------------------------------------------------------------- /cp/channels/pingpong/pingpong.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "time" 8 | ) 9 | 10 | // Ball contains the number of hits. 11 | type Ball struct{ hits int } 12 | 13 | func main() { 14 | table := make(chan *Ball) 15 | go player("ping", table) 16 | go player("pong", table) 17 | 18 | table <- new(Ball) // game on; toss the ball 19 | time.Sleep(1 * time.Second) 20 | <-table // game over; grab the ball 21 | } 22 | 23 | func player(name string, table chan *Ball) { 24 | for { 25 | ball := <-table 26 | ball.hits++ 27 | fmt.Println(name, ball.hits) 28 | time.Sleep(100 * time.Millisecond) 29 | table <- ball 30 | } 31 | } 32 | 33 | // EOF OMIT 34 | -------------------------------------------------------------------------------- /cp/locks/README.md: -------------------------------------------------------------------------------- 1 | ![Logo](../../doc/article/img/Logo.png "Logo") 2 | ### *Concurrency with Go* 3 | 4 | - Blocking Queue 5 | - Resource Manager 6 | 7 | ## Using the go sync package to write a Java like Blocking Queue. 8 | **Go Channels are great. No Question. But sometimes you want to have more control about when to block and what to do if some condition is reached.** 9 | The Go sync package offers the same possibilities as java.util.concurrent does. 10 | 11 | 12 | ```go 13 | import "sync" 14 | 15 | // BlockingQueue is a FIFO container with a fixed capacity. 16 | // It blocks a reader when it is empty and a writer when it is full. 17 | type BlockingQueue struct { 18 | m sync.Mutex 19 | c sync.Cond 20 | data []interface{} 21 | capacity int 22 | } 23 | ``` 24 | 25 | ```go 26 | // NewBlockingQueue constructs a BlockingQuee with a given capacity. 27 | func NewBlockingQueue(capacity int) *BlockingQueue { 28 | q := new(BlockingQueue) 29 | q.c = sync.Cond{L: &q.m} 30 | q.capacity = capacity 31 | return q 32 | } 33 | ``` 34 | 35 | ```go 36 | // Put puts an item in the queue and blocks it the queue is full. 37 | func (q *BlockingQueue) Put(item interface{}) { 38 | q.c.L.Lock() 39 | defer q.c.L.Unlock() 40 | 41 | for q.isFull() { 42 | q.c.Wait() 43 | } 44 | q.data = append(q.data, item) 45 | q.c.Signal() 46 | } 47 | ``` 48 | 49 | 50 | ```go 51 | // Take takes an item from the queue and blocks if the queue is empty. 52 | func (q *BlockingQueue) Take() interface{} { 53 | q.c.L.Lock() 54 | defer q.c.L.Unlock() 55 | 56 | for q.isEmpty() { 57 | q.c.Wait() 58 | } 59 | result := q.data[0] 60 | q.data = q.data[1 : len(q.data)] 61 | q.c.Signal() 62 | return result 63 | } 64 | ``` 65 | 66 | ```go 67 | // isFull returns true if the capacity is reached. 68 | func (q *BlockingQueue) isFull() bool { 69 | return len(q.data) == q.capacity 70 | } 71 | 72 | // isEmpty returns true if there are no elements in the queue. 73 | func (q *BlockingQueue) isEmpty() bool { 74 | return len(q.data) == 0 75 | } 76 | ``` 77 | 78 | 79 | ```go 80 | func TestBlockingQueue(t *testing.T) { 81 | 82 | bq := NewBlockingQueue(1) 83 | done := make(chan bool) 84 | 85 | // slow writer 86 | go func() { 87 | bq.Put("A") 88 | time.Sleep(100 * time.Millisecond) 89 | bq.Put("B") 90 | time.Sleep(100 * time.Millisecond) 91 | bq.Put("C") 92 | }() 93 | 94 | // reader will be blocked 95 | go func() { 96 | item := bq.Take() 97 | fmt.Printf("Got %v\n", item) 98 | item = bq.Take() 99 | fmt.Printf("Got %v\n", item) 100 | item = bq.Take() 101 | fmt.Printf("Got %v\n", item) 102 | done <- true 103 | }() 104 | 105 | // block while done 106 | <-done 107 | } 108 | ``` -------------------------------------------------------------------------------- /cp/locks/blockingqueue/blockingqueue.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend, copied from Johannes Siedersleben 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package blockingqueue contains a blocking LIFO container. 5 | package blockingqueue 6 | 7 | import "sync" 8 | 9 | // BlockingQueue is a FIFO container with a fixed capacity. 10 | // It blocks a reader when it is empty and a writer when it is full. 11 | type BlockingQueue struct { 12 | m sync.Mutex 13 | c sync.Cond 14 | data []interface{} 15 | capacity int 16 | } 17 | 18 | // NewBlockingQueue constructs a BlockingQuee with a given capacity. 19 | func NewBlockingQueue(capacity int) *BlockingQueue { 20 | q := new(BlockingQueue) 21 | q.c = sync.Cond{L: &q.m} 22 | q.capacity = capacity 23 | return q 24 | } 25 | 26 | // A1 27 | 28 | // Put puts an item in the queue and blocks it the queue is full. 29 | func (q *BlockingQueue) Put(item interface{}) { 30 | q.c.L.Lock() 31 | defer q.c.L.Unlock() 32 | 33 | for q.isFull() { 34 | q.c.Wait() 35 | } 36 | q.data = append(q.data, item) 37 | q.c.Signal() 38 | } 39 | 40 | // Take takes an item from the queue and blocks if the queue is empty. 41 | func (q *BlockingQueue) Take() interface{} { 42 | q.c.L.Lock() 43 | defer q.c.L.Unlock() 44 | 45 | for q.isEmpty() { 46 | q.c.Wait() 47 | } 48 | result := q.data[0] 49 | q.data = q.data[1:len(q.data)] 50 | q.c.Signal() 51 | return result 52 | } 53 | 54 | // A2 55 | 56 | // isFull returns true if the capacity is reached. 57 | func (q *BlockingQueue) isFull() bool { 58 | return len(q.data) == q.capacity 59 | } 60 | 61 | // isEmpty returns true if there are no elements in the queue. 62 | func (q *BlockingQueue) isEmpty() bool { 63 | return len(q.data) == 0 64 | } 65 | -------------------------------------------------------------------------------- /cp/locks/blockingqueue/blockingqueue_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | package blockingqueue 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func TestBlockingQueue(t *testing.T) { 13 | 14 | bq := NewBlockingQueue(1) 15 | done := make(chan bool) 16 | 17 | // slow writer 18 | go func(bq *BlockingQueue) { 19 | bq.Put("A") 20 | time.Sleep(100 * time.Millisecond) 21 | bq.Put("B") 22 | time.Sleep(100 * time.Millisecond) 23 | bq.Put("C") 24 | }(bq) 25 | 26 | // reader will be blocked 27 | go func(bq *BlockingQueue) { 28 | item := bq.Take() 29 | fmt.Printf("Got %v\n", item) 30 | item = bq.Take() 31 | fmt.Printf("Got %v\n", item) 32 | item = bq.Take() 33 | fmt.Printf("Got %v\n", item) 34 | done <- true 35 | }(bq) 36 | 37 | // block while done 38 | <-done 39 | } 40 | 41 | // EOF OMIT 42 | -------------------------------------------------------------------------------- /cp/locks/philosophers/philosophers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package philosophers 16 | 17 | import ( 18 | "fmt" 19 | "log" 20 | "time" 21 | ) 22 | 23 | // Philosopher represents one philosopher. 24 | type Philosopher struct { 25 | id int 26 | table *Table 27 | } 28 | 29 | // NewPhilosopher constructs a philosopher. 30 | func NewPhilosopher(id int, table *Table) *Philosopher { 31 | p := new(Philosopher) 32 | p.id = id 33 | p.table = table 34 | return p 35 | } 36 | 37 | // Run loops forever. 38 | func (p *Philosopher) run() { 39 | for { 40 | p.takeForks() 41 | p.eat() 42 | p.putForks() 43 | p.think() 44 | } 45 | } 46 | 47 | // Take forks by channeling our id to the table and wait until the table returns true on the reserved channel. 48 | func (p *Philosopher) takeForks() { 49 | manager := p.table.GetManager() 50 | // Names 51 | ph := fmt.Sprintf("P%v", p.id) 52 | f1 := fmt.Sprintf("F%v", p.id) 53 | f2 := fmt.Sprintf("F%v", (p.id+1)%p.table.nbrOfSeats) 54 | 55 | // take forks 56 | gotForks := false 57 | for !gotForks { 58 | gotForks = manager.Acquire(ph, f1) 59 | if gotForks { 60 | gotForks = manager.Acquire(ph, f2) 61 | if !gotForks { // deadlock detected 62 | manager.Release(ph, f1) 63 | } 64 | } else { 65 | log.Println("Deadlock detected -> try again") 66 | } 67 | } 68 | 69 | } 70 | 71 | // Put forks by channeling our id to the table. The table is responsible for the put logic. 72 | func (p *Philosopher) putForks() { 73 | 74 | manager := p.table.GetManager() 75 | ph := fmt.Sprintf("P%v", p.id) 76 | f1 := fmt.Sprintf("F%v", p.id) 77 | f2 := fmt.Sprintf("F%v", (p.id+1)%p.table.nbrOfSeats) 78 | 79 | // put forks 80 | manager.Release(ph, f2) 81 | manager.Release(ph, f1) 82 | } 83 | 84 | // Eating. 85 | func (p *Philosopher) eat() { 86 | fmt.Printf("[->]: Philosopher #%d eats ...\n", p.id) 87 | time.Sleep(10 * time.Millisecond) 88 | fmt.Printf("[<-]: Philosopher #%d eat ends.\n", p.id) 89 | } 90 | 91 | // Thinking. 92 | func (p *Philosopher) think() { 93 | fmt.Printf("[->]: Philosopher #%d thinks ...\n", p.id) 94 | time.Sleep(30 * time.Millisecond) 95 | fmt.Printf("[<-]: Philosopher #%d thinking ends\n", p.id) 96 | } 97 | -------------------------------------------------------------------------------- /cp/locks/philosophers/philosophers_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package philosophers 15 | 16 | import ( 17 | "testing" 18 | "time" 19 | ) 20 | 21 | // ===================================================================================================================== 22 | // Main Test. 23 | // ===================================================================================================================== 24 | 25 | func TestPhilosophers(t *testing.T) { 26 | 27 | var COUNT = 5 28 | 29 | // start table for 5 30 | table := NewTable(COUNT) 31 | 32 | // start philosophers 33 | for i := 0; i < COUNT; i++ { 34 | philosopher := NewPhilosopher(i, table) 35 | go philosopher.run() 36 | } 37 | 38 | // wait 1 millisecond --> check output 39 | time.Sleep(1000 * time.Millisecond) 40 | } 41 | -------------------------------------------------------------------------------- /cp/locks/philosophers/table.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package philosophers implements the Dining Philosophers Problem. 16 | package philosophers 17 | 18 | import ( 19 | "github.com/jweigend/concepts-of-programming-languages/cp/locks/resourcemanager" 20 | ) 21 | 22 | // ===================================================================================================================== 23 | // Dining Philosophers Problem. See https://en.wikipedia.org/wiki/Dining_philosophers_problem. 24 | // The synchronization is done with channels. There are two channels for put and request fork operation. 25 | // There is one channel per philosopher to signal when the forks can be taken. 26 | // ===================================================================================================================== 27 | 28 | // Table represents the table with dynamic seat count. 29 | type Table struct { 30 | manager *resourcemanager.ResourceManager 31 | forkInUse []bool 32 | nbrOfSeats int 33 | } 34 | 35 | // NewTable constructs a table with n seats. 36 | func NewTable(nbrOfSeats int) *Table { 37 | table := new(Table) 38 | table.manager = resourcemanager.NewResourceManager() 39 | table.forkInUse = make([]bool, nbrOfSeats) 40 | table.nbrOfSeats = nbrOfSeats 41 | return table 42 | } 43 | 44 | // GetManager returns the resource manager. 45 | func (t *Table) GetManager() *resourcemanager.ResourceManager { 46 | return t.manager 47 | } 48 | -------------------------------------------------------------------------------- /cp/locks/resourcemanager/README.md: -------------------------------------------------------------------------------- 1 | ## *Concurrency with Go - Resource Manager* 2 | 3 | ResourceManager implements a deadlock avoidance strategy by finding cycles in a Ressource Allocation Graph. 4 | It blocks requests, when a resource is in use. The user is responsible to implement a retry strategy when a deadlock is recognized. 5 | 6 | The Resource Manager has two operations: 7 | 8 | 1. Acquire(processName, resourceName) bool 9 | 2. Release(processName, resourceName) 10 | 11 | If a resource is already in use, the Acquire() method blocks until the resource is free. 12 | The Acquire() method returns true if the resource could be claimed. In case of a deadlock the method returns false. 13 | It is up to the caller to retry the operation, if the resource is in use and the claim will produce a deadlock operation. 14 | 15 | ### Sample 16 | 17 | ``` 18 | Acquire ("P1", "R1" ) : P1 <- R1 (ok) - Process 1 got Resource 1 19 | Acquire ("P1", "R3" ) : P1 <- R3 (ok) - Process 1 got Resource 3 20 | Acquire ("P2", "R2" ) : P2 <- R2 (ok) - Process 2 got Resource 2 21 | Acquire ("P2", "R1" ) : P2 -> R1 (wait) - Process 2 cant get Resource 1 (in use by Process 1) : wait 22 | Acquire ("P1", "R2" ) returns false : P1 -> R2 (deadlock) - acquire will recognize the deadlock and returns false 23 | ``` 24 | 25 | ### Dining Philosophers 26 | ```go 27 | // take forks 28 | for !gotForks { 29 | gotForks := manager.Acquire("P" + id, "F" + id)) 30 | if gotForks { 31 | gotForks = manager.Acquire("P" + id, "F" + (id + 1) % COUNT) 32 | if !gotForks { // deadlock detected, release fork 33 | m.Release("P" + id, "F" + id)) 34 | } 35 | } else { 36 | // deadlock detected -> try again 37 | } 38 | } 39 | 40 | // eat 41 | 42 | // put forks 43 | manager.Release("P" + id, "F" + ((id + 1) % COUNT)) 44 | manager.Release("P" + id, "F" + id) 45 | ``` 46 | -------------------------------------------------------------------------------- /cp/locks/resourcemanager/resourcegraph.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package resourcemanager contains a blocking resource manger which avoids deadlocks 5 | package resourcemanager 6 | 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | ) 11 | 12 | // ResourceGraph is a simple RAG ResourceAllocationGraph. 13 | // A graph is a collection of edges. Each edge is of the form 14 | // edge := source -> [target1, target2, ... targetN] 15 | type ResourceGraph struct { 16 | edges map[string][]string 17 | } 18 | 19 | // NewResourceGraph creates an empty graph. 20 | func NewResourceGraph() *ResourceGraph { 21 | resourceGraph := new(ResourceGraph) 22 | resourceGraph.edges = make(map[string][]string) 23 | return resourceGraph 24 | } 25 | 26 | // AddLink adds a link to the graph. 27 | func (r *ResourceGraph) AddLink(source, dest string) { 28 | destinations := r.edges[source] 29 | destinations = append(destinations, dest) 30 | r.edges[source] = destinations 31 | } 32 | 33 | // RemoveLink removes a link from the graph. 34 | func (r *ResourceGraph) RemoveLink(source, dest string) { 35 | destinations := r.edges[source] 36 | destinations = removeItem(destinations, dest) 37 | if len(destinations) > 0 { 38 | r.edges[source] = destinations 39 | } else { 40 | delete(r.edges, source) // remove entry complete from map 41 | } 42 | } 43 | 44 | // Get destinations for a given source. 45 | func (r *ResourceGraph) Get(source string) []string { 46 | destinations := r.edges[source] 47 | return destinations 48 | } 49 | 50 | // DetectCycle reports true if there is a cycle between source and dest 51 | func (r *ResourceGraph) DetectCycle(source string, dest string) bool { 52 | return r.detectCycle1(source, source, dest) 53 | } 54 | 55 | // traverse the graph and check if dest is reachable from first 56 | func (r *ResourceGraph) detectCycle1(first string, source string, dest string) bool { 57 | 58 | if first == dest { 59 | return true 60 | } 61 | result := false 62 | destinations := r.edges[source] 63 | if destinations == nil { 64 | return result 65 | } 66 | 67 | for _, element := range destinations { 68 | result = result || r.detectCycle1(first, dest, element) 69 | } 70 | 71 | return result 72 | } 73 | 74 | // Stringer implementation. 75 | func (r *ResourceGraph) String() string { 76 | b, _ := json.MarshalIndent(r.edges, "", " ") 77 | return fmt.Sprintf("%v", string(b)) 78 | } 79 | 80 | // Helper removes an item from a string list. 81 | func removeItem(list []string, item string) []string { 82 | 83 | for i := len(list) - 1; i >= 0; i-- { 84 | v := list[i] 85 | if v == item { 86 | // found 87 | list[i] = list[len(list)-1] 88 | return list[0 : len(list)-1] 89 | } 90 | } 91 | return list // not found 92 | } 93 | -------------------------------------------------------------------------------- /cp/locks/resourcemanager/resourcegraph_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | package resourcemanager 4 | 5 | import ( 6 | "fmt" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | // TestResourceGraph tests the resource graph. 12 | func TestResourceGraph(t *testing.T) { 13 | graph := NewResourceGraph() 14 | 15 | graph.AddLink("a", "b") 16 | graph.AddLink("a", "c") 17 | graph.AddLink("b", "c") 18 | 19 | // check no cycle 20 | if graph.DetectCycle("c", "a") { 21 | t.Error("c->a is not a cycle!") 22 | } 23 | 24 | // add a cycle 25 | graph.AddLink("c", "a") 26 | if !graph.DetectCycle("c", "a") { 27 | t.Error("c->a is a cycle!") 28 | } 29 | 30 | graph.RemoveLink("c", "a") 31 | if graph.DetectCycle("c", "a") { 32 | t.Error("c->a is not a cycle!") 33 | } 34 | } 35 | 36 | func TestSimpleResourceManager(t *testing.T) { 37 | manager := NewResourceManager() 38 | manager.Acquire("P1", "R1") 39 | manager.Acquire("P2", "R2") 40 | go manager.Acquire("P1", "R2") // should block 41 | fmt.Printf("%v", manager) 42 | manager.Release("P2", "R2") 43 | time.Sleep(10 * time.Millisecond) 44 | fmt.Printf("%v", manager) 45 | } 46 | 47 | // TestResourceManager tests the manager which blocks if a resource is in use and no deadlock is detected. 48 | // Acquire ("P1", "R1" ) : P1 <- R1 (ok) - Process 1 got Resource 1 49 | // Acquire ("P1", "R3" ) : P1 <- R3 (ok) - Process 1 got Resource 3 50 | // Acquire ("P2", "R2" ) : P2 <- R2 (ok) - Process 2 got Resource 2 51 | // Acquire ("P2", "R1" ) : P2 -> R1 (wait) - Process 2 cant get Resource 1 (in use by Process 1) : wait 52 | // Acquire ("P1", "R2" ) returns false : P1 -> R2 (deadlock) - acquire will recognize the deadlock and raturns false 53 | func TestResourceManager(t *testing.T) { 54 | 55 | manager := NewResourceManager() 56 | p1 := make(chan bool) 57 | p2 := make(chan bool) 58 | 59 | go func() { 60 | manager.Acquire("P1", "R1") 61 | manager.Acquire("P1", "R3") 62 | p1 <- true 63 | }() 64 | // block p1 until finished 65 | <-p1 66 | 67 | go func() { 68 | manager.Acquire("P2", "R2") 69 | p2 <- false 70 | manager.Acquire("P2", "R1") // waits 71 | p2 <- true 72 | }() 73 | 74 | if manager.Acquire("P1", "R1") { 75 | t.Error("P1/R2 should detect a deadlock and return false") 76 | } 77 | 78 | if <-p2 { 79 | t.Error("P2 should block when requesting R1") 80 | } 81 | 82 | manager.Release("P1", "R1") 83 | 84 | if !<-p2 { 85 | t.Error("P2 should not block anymore when requesting R1") 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /cp/locks/resourcemanager/resourcemanager.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | package resourcemanager 5 | 6 | import ( 7 | "errors" 8 | "fmt" 9 | "sync" 10 | ) 11 | 12 | // ResourceManager detects deadlocks by finding cycles in a Ressource Allocation Graph. 13 | // blocks, when a resource is in use. The user is responsible to implement a resource release strategy, when a deadlock is recognized. 14 | // Sample 15 | // Acquire ("P1", "R1" ) : P1 <- R1 (ok) - Process 1 got Resource 1 16 | // Acquire ("P1", "R3" ) : P1 <- R3 (ok) - Process 1 got Resource 3 17 | // Acquire ("P2", "R3" ) : P2 <- R2 (ok) - Process 2 got Resource 2 18 | // Acquire ("P2", "R1" ) : P2 -> R1 (wait) - Process 2 cant get Resource 1 (in use by Process 1) : wait 19 | // Acquire ("P1", "R2" ) returns false : P1 -> R2 (deadlock) - acquire will recognize the deadlock and raturns false 20 | type ResourceManager struct { 21 | graph *ResourceGraph 22 | m sync.Mutex 23 | c sync.Cond 24 | } 25 | 26 | // NewResourceManager creates a Resource Manager. 27 | func NewResourceManager() *ResourceManager { 28 | manager := new(ResourceManager) 29 | manager.c = sync.Cond{L: &manager.m} 30 | manager.graph = NewResourceGraph() 31 | return manager 32 | } 33 | 34 | // Acquire tries to acquire a resource. The method blocks as long as the given resource is in use. 35 | // The implementation recognizes a deadlock situation. In this case the method returns false. 36 | // @param processName The name of the process. 37 | // @param resourceName The name of the resource. 38 | // @return true if the Resource could be acquired for the given process. 39 | // false if a deadlock is detected. 40 | // 41 | func (r *ResourceManager) Acquire(processName string, resourceName string) bool { 42 | r.m.Lock() 43 | defer r.m.Unlock() 44 | 45 | if resourceName == "" || processName == "" { 46 | panic(errors.New("processname or resourcename can not be empty")) 47 | } 48 | 49 | r.graph.AddLink(processName, resourceName) // add Px -> Ry 50 | 51 | for r.resourceIsInUse(resourceName, processName) { 52 | if r.graph.DetectCycle(processName, resourceName) { 53 | r.graph.RemoveLink(processName, resourceName) 54 | return false // Deadlock detected 55 | } 56 | // log.Printf("Blocking %v, %v, %v", resourceName, processName, r.graph) 57 | r.c.Wait() 58 | } 59 | 60 | r.graph.RemoveLink(processName, resourceName) // remove Px -> Ry 61 | r.graph.AddLink(resourceName, processName) // add Ry -> Px 62 | 63 | return true // no deadlock 64 | } 65 | 66 | // 67 | // Release the resource for a given process by removing it from the process-resource-graph. 68 | // 69 | func (r *ResourceManager) Release(processName string, resourceName string) { 70 | r.m.Lock() 71 | defer r.m.Unlock() 72 | 73 | r.graph.RemoveLink(resourceName, processName) 74 | r.c.Signal() 75 | } 76 | 77 | /** 78 | * A resource is in use when a process owns the resource : 79 | * R1 -> [Px] 80 | */ 81 | func (r *ResourceManager) resourceIsInUse(resourceName string, processName string) bool { 82 | process := r.graph.Get(resourceName) 83 | if process == nil { 84 | return false 85 | } 86 | return len(process) == 1 87 | } 88 | 89 | func (r *ResourceManager) String() string { 90 | return fmt.Sprintf("%v", r.graph.String()) 91 | } 92 | -------------------------------------------------------------------------------- /docs/1.0-About.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/1.0-About.pdf -------------------------------------------------------------------------------- /docs/1.0-About.slide: -------------------------------------------------------------------------------- 1 | Welcome 2 | Concepts of Programming Languages - TH Rosenheim - WS 2010/2020 3 | 10 Oct 2019 4 | Tags: go, programming, master 5 | 6 | Johannes Weigend 7 | QAware GmbH 8 | johannes.weigend@qaware.de 9 | http://www.qaware.de 10 | @johannesweigend 11 | 12 | * There are so many Languages ... 13 | .image ./img/00-programming-languages.jpeg 550 1000 14 | 15 | * Course Requirements 16 | - The course is designed as a master course. 17 | - Solid programming skills in Java/C/C++ are required. 18 | - It is assumed that students have some skills in Scala, Python or Ruby. 19 | - For all code examples, we will use Go (Golang) as language. 20 | 21 | * Goal of the Course 22 | 23 | .image img/go.png 24 | - Learn how Go differs from other languages conceptually 25 | - Gain skills to pick the right language for a given problem 26 | - Knowledge about the concepts of existing programming languages 27 | - Learn how to write professional code with Golang 28 | 29 | * Lectures 30 | 31 | Lecture 1 - *Introduction* 32 | 33 | Lecture 2 - *Basic* *Language* *Overview* 34 | 35 | Lecture 3 - *OOP* *with* *Go* 36 | 37 | Lecture 4 - *Functional* *Programming* 38 | 39 | Lecture 5 - *Fuctional* *Parsers* 40 | 41 | Lecture 6 - *Parallel* *Programming* - Part I 42 | 43 | Lecture 7 - *Parallel* *Programming* - Part II 44 | 45 | Lecture 8 - *Distributed* *Programming* - Part I 46 | 47 | Lecture 9 - *Distributed* *Programming* - Part II 48 | 49 | Lecture 10 - *Microservices* 50 | 51 | * Lectures 52 | 53 | Lecture 11 - *Systems* *Programming* 54 | 55 | Lecture 12 - *Summary* 56 | 57 | 58 | * Structure of this course 59 | - Recap - 15 min 60 | - Lecture - 30 min 61 | - Online Tutorials (Video) - 45 min 62 | - Student Discussions - 30 min 63 | - Introduction to Exercises - 15 min 64 | - Exercises - 45 min (+ Homework) 65 | 66 | * Material 67 | Learning Campus: PDFs only 68 | .link https://learning-campus.fh-rosenheim.de/course/view.php?id=1412 69 | 70 | Github: PDFs, Sources, Slides (Go Present Tool) 71 | .link https://github.com/jweigend/concepts-of-programming-languages 72 | 73 | 74 | * Semester Work 75 | 15 Minutes presentation + 5-10 pages Asciidoc document (written in English) 76 | 77 | - Compare Go *OOP* (Object Oriented Programming) with: 78 | Smalltalk 79 | C++ 80 | Eiffel 81 | Objective C 82 | Modula 83 | Lua 84 | 85 | - Compare Go *FP* (Functional Programming) with: 86 | Haskell 87 | Clojure 88 | F# 89 | 90 | * Semester Work 91 | 92 | - Compare Go *SP* (Systems Programming) with: 93 | C 94 | C++ 95 | Rust 96 | Ada 97 | D 98 | Swift 99 | 100 | - Compare Go *Concurrency* with: 101 | Erlang 102 | Scala Actors 103 | D 104 | Occam 105 | 106 | * Semester Work 107 | 108 | - Compare Go *Distributed* *Programming* with: 109 | JavaScript / NodeJS 110 | C++ 111 | Java / Quarkus 112 | Python 113 | 114 | - Compare *Go* with: 115 | Typescript 116 | Ruby 117 | Python 118 | Kotlin 119 | Elm 120 | Elixir 121 | Crystal 122 | 123 | 124 | * Books 125 | Donovan, Kernigham: The Go Programming Language 126 | .image https://d3by36x8sj6cra.cloudfront.net/assets/images/book/large/9780/1341/9780134190440.jpg 200 200 127 | 128 | Sebesta: Concepts of Programming Languages 129 | .image https://img.yumpu.com/59461192/1/358x441/concepts-of-programming-languages-robert-w-sebesta.jpg?quality=85 200 200 -------------------------------------------------------------------------------- /docs/1.1-Overview.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/1.1-Overview.pdf -------------------------------------------------------------------------------- /docs/1.1-Overview.slide: -------------------------------------------------------------------------------- 1 | Concepts of Programming Languages 2 | Overview 3 | 04 Oct 2018 4 | Tags: go, programming, master 5 | 6 | Johannes Weigend 7 | QAware GmbH 8 | johannes.weigend@qaware.de 9 | http://www.qaware.de 10 | @johannesweigend 11 | 12 | * Evolution of Programming Languages 13 | .link img/01-Languages.png 14 | .image img/01-Languages.png 500 1000 15 | 16 | * Types of Programming Languages 17 | - *Imperative*: Assembler as root (Operations and Control Structures) 18 | - *Declarative:* Domain Specific Languages (HTML, XML, YAML, ...) => Limited in scope. Some are not Turing Complete. 19 | - *Procedural* (Control Structures and Procedures): Fortran / Algol / Pascal / *C* 20 | - *Object* *Oriented* (Classes and Objects): Smalltalk / Objective C / C++ / Eifel / *Java* / C# 21 | - *Functional* (Lambdas + Closures): Haskell, Erlang, F# 22 | - *Logic*: Prolog 23 | - *Best* *of* *Breed*: Kotlin, Scala, Rust, JS, *Go* 24 | .link https://en.wikipedia.org/wiki/List_of_programming_languages_by_type More Languages 25 | 26 | * Areas for Programming Languages 27 | - Application Programming (small / large scale, web, central, cloud) 28 | - Systems Programming 29 | - Network Programming / Distributed Systems 30 | - Realtime Systems 31 | - User Interface Programming 32 | - Embedded Systems 33 | 34 | * Orthogonal Classifications 35 | - Static vs. Dynamic Typed 36 | - Compiled vs. Interpreted 37 | - Sequential vs. Parallel 38 | - Static Linked vs. Dynamic Linked 39 | - Safe vs. Unsafe (Crash a process or even the system) 40 | - Simple vs. Complex 41 | - Platform Independent Bytecode vs. Platform Dependent Assemblercode 42 | - Plattform Dependent Compiler vs. Cross Compiler 43 | 44 | * Some Thoughts 45 | - There are many solutions for a given problem 46 | - The "best" language is nothing without a proper ecosystem (Libraries, Examples, Community) 47 | - There are straight forward choices for a given platform (Mac: Swift + Objectiv-C, Linux: C + Python, Android: Java) 48 | - Dont expect "Magic" from programming Languages 49 | - Clean code and good programming is orthogonal to the language 50 | - Learn more than one programming language at a professional level 51 | -------------------------------------------------------------------------------- /docs/1.2-Introduction to Golang.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/1.2-Introduction to Golang.pdf -------------------------------------------------------------------------------- /docs/1.2-Introduction to Golang.slide: -------------------------------------------------------------------------------- 1 | Introduction to Golang 2 | Concepts of Programming Languages 3 | 04 Oct 2018 4 | Tags: go, programming, master 5 | 6 | Johannes Weigend 7 | QAware GmbH 8 | johannes.weigend@qaware.de 9 | http://www.qaware.de 10 | @johannesweigend 11 | 12 | * Gopher 13 | .image https://talks.golang.org/2012/10things/gopher.jpg 14 | 15 | * Why Go? 16 | - Go is the major language behind the Cloud Native Stack 17 | .link https://www.cncf.io/ 18 | .image ./img/01-cncf-projects.png 100 1024 19 | - The most important components are written in Go: Docker, Kubernetes, etcd, Prometheus, Grafana, ... 20 | - Go is Open Source and maintained by Google 21 | - Go is a distributed, parallel language designed for systems programming at Google to solve the problems of C++ code. 22 | 23 | * Hello World 24 | .play ../basics/hello/main.go 25 | 26 | * Palindrome 27 | .code ../basics/palindrome/palindrome.go /IsPalindrome/,/END1 OMIT/ 28 | .code ../basics/palindrome/palindrome_test.go /START OMIT/,/END OMIT/ 29 | 30 | * Introduction to Golang 31 | Rob Pike @ Google 2009 (60 Min) 32 | .link https://www.youtube.com/watch?v=rKnDgT73v8s 33 | .image img/01-go-programming-Language.png 450 700 34 | 35 | * Some Questions 36 | - What makes Go different to other Languages? 37 | - What makes Go similar to other Languages? 38 | - Discuss your personal opinions in a group of students! 39 | 40 | * Exercise 1 41 | .link https://github.com/jweigend/concepts-of-programming-languages/blob/master/docs/exercises/Exercise1.md 42 | .image img/01-exercise.png 500 700 43 | 44 | * See also 45 | 46 | .link https://github.com/jweigend/concepts-of-programming-languages 47 | .link https://golang.org/ 48 | .link https://golang.org/doc/ 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /docs/2.0-Go Programming - Basics and OOP.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/2.0-Go Programming - Basics and OOP.pdf -------------------------------------------------------------------------------- /docs/3.0-Go-Programming-OOP.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/3.0-Go-Programming-OOP.pdf -------------------------------------------------------------------------------- /docs/3.0-Go-Programming-OOP.slide: -------------------------------------------------------------------------------- 1 | Go Programming - OOP Part II 2 | Concepts of Programming Languages 3 | 24 Oct 2019 4 | Tags: go, programming, master 5 | 6 | 7 | Johannes Weigend 8 | Rosenheim Technical University 9 | johannes.weigend@qaware.de 10 | http://www.qaware.de 11 | 12 | * Embedding 13 | - Go does not support inheritance: Go supports embedding of other structs. 14 | .code ../oop/polymorphism/polymorphism.go /Point/,8 15 | 16 | .code ../oop/polymorphism/polymorphism.go /ColorPoint/,19 17 | 18 | .code ../oop/polymorphism/polymorphism.go /cp.x/,35 19 | 20 | - In Java this can be done with delegation. 21 | - Syntactically it is similar to inheritance in Java 22 | - Access to embedded field is identical to a normal field inside a struct 23 | - Overriding of methods is supported, overloading is not! 24 | 25 | * Interfaces and Polymorphism 26 | .play ../oop/polymorphism/polymorphism.go /func main/,/END2 OMIT/ 27 | 28 | * Send Mail with Go: A minimal Interface 29 | .code ../oop/mail/mail.go /Address/,/END OMIT/ 30 | - A example interface for a service-oriented component 31 | 32 | * A type implements an interface when providing the required methods 33 | .code ../oop/mail/smtp/sender.go /Package/,/END OMIT/ 34 | - Import references fully qualified VC directories in $GOPATH/src 35 | 36 | * The Go interface can be used as in Java 37 | .code ../oop/mail/client/client.go /Package/,/EOF OMIT/ 38 | 39 | * Summary 40 | - Several interfaces can be put together to form an interface 41 | - Go does not support inheritance but type embedding (delegation without syntactic ballast) 42 | - Go supports polymorphism only via interfaces, not through classes 43 | - Interfaces with one method end with the ending "er" (Stringer, Writer, Reader...) 44 | .link https://youtu.be/Ng8m5VXsn8Q?t=414 45 | 46 | * Exercise 3 47 | .image img/03-exercise.png 600 800 48 | 49 | * Exercise 50 | - Implement the UML diagram with Go 51 | - The Paint() method should print the names and values of the fields to the console 52 | - Allocate an array of polymorph objects and call Paint() in a loop 53 | .link https://github.com/jweigend/concepts-of-programming-languages/blob/master/docs/exercises/Exercise3.md 54 | 55 | * Questions 56 | - What is the difference between inheritance in Java and embedding in Go? 57 | - How does Go support multiple inheritance? Is is supported for interfaces and types? 58 | -------------------------------------------------------------------------------- /docs/4.0-Go-Programming-Parser.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/4.0-Go-Programming-Parser.pdf -------------------------------------------------------------------------------- /docs/5.0-Functional-Programming.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/5.0-Functional-Programming.pdf -------------------------------------------------------------------------------- /docs/6.0-Concurrent-Programming.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/6.0-Concurrent-Programming.pdf -------------------------------------------------------------------------------- /docs/6.0-Concurrent-Programming.slide: -------------------------------------------------------------------------------- 1 | Concurrent Programming with Go 2 | Concepts of Programming Languages 3 | 14 Nov 2019 4 | Tags: go, programming, concurrent, go routines, channels 5 | 6 | Johannes Weigend 7 | Rosenheim Technical University 8 | johannes.weigend@qaware.de 9 | http://www.qaware.de 10 | 11 | * Why Concurrent Programming? 12 | - Computer clock rates do not get higher anymore (since 2004!) 13 | - But Moores Law is still valid (Multicore!) 14 | .image img/06-moores-law.png 450 800 15 | 16 | * The modern world is parallel 17 | 18 | Multicore. 19 | 20 | Networks. 21 | 22 | Clouds of CPUs. 23 | 24 | Loads of users. 25 | 26 | * Concurrent Programming with Go 27 | .image img/06-go-concurrency.jpeg 28 | - Don't communicate by sharing memory; share memory by communicating (Rob Pike) 29 | 30 | * Go provides: 31 | - concurrent execution (goroutines) 32 | - synchronization and messaging (channels) 33 | - multi-way concurrent control (select) 34 | - low level blocking primitives (locks) - Usually not needed! 35 | 36 | * Goroutines 37 | 38 | A goroutine is a function running independently in the same address space as other goroutines 39 | 40 | .code snippets/cp /f.runs/ 41 | 42 | .code snippets/cp /f.starts.running/,/return/ 43 | 44 | Like launching a function with shell's `&` notation. 45 | 46 | * Goroutines are not threads 47 | 48 | (They're a bit like threads, but they're much cheaper.) 49 | 50 | Goroutines are multiplexed onto OS threads as required. 51 | 52 | When a goroutine blocks the thread will execute other goroutines 53 | 54 | IO Calls and calls calls to the Go Standard Library trigger the scheduler 55 | 56 | -> There are no thread local variables in Go 57 | 58 | * Channels 59 | - Go routines can use channels for safe communication 60 | - Construct a channel 61 | c := make(chan int) // buffer size = 0 62 | c := make(chan int, 10) // buffer size = 10 63 | - Send to channel 64 | c <- 1 65 | - Read from channel 66 | x = <- c 67 | - size = 0 (=default): Sender blocks until a reader requests a value from the channel 68 | - size = n: Sender is not blocked until the buffer size is reached 69 | 70 | * Channels 71 | 72 | Channels are typed values that allow goroutines to synchronize and exchange information. 73 | 74 | .code snippets/cp /make.*chan/,/completedAt/ 75 | 76 | * Ping Pong 77 | .play ../cp/channels/pingpong/pingpong.go /Ball/,/EOF/ 78 | 79 | * Channel and Errors 80 | - Channel can be closed. Readers will return immediately. Successive writes will cause panic. 81 | close(c) 82 | - If a channel was closed, the reader gets "false" as return code (second return value) 83 | x, rc := <-c 84 | - Reading from a channel until closed 85 | for { 86 | x, ok := <-c 87 | if !ok { 88 | break 89 | } 90 | // do something with x 91 | } 92 | // channel closed 93 | 94 | 95 | * Fan Out 96 | - Read tasks from a channel and start parallel processing. Results will be written in a result channel. 97 | .code ../cp/channels/fan/fanout.go /FanOut/,/EOF OMIT/ 98 | 99 | * Fan In 100 | - Merge n channels into one 101 | .code ../cp/channels/fan/fanin.go /FanIn/,/FanIn OMIT/ 102 | 103 | 104 | * Select 105 | 106 | The `select` statement is like a `switch`, but the decision is based on ability to communicate rather than equal values. 107 | 108 | .code snippets/cp /select/,/}/ 109 | 110 | * Go really supports concurrency 111 | 112 | Really. 113 | 114 | It's routine to create thousands of goroutines in one program. 115 | (Once debugged a program after it had created 1.3 million.) 116 | 117 | Stacks start small, but grow and shrink as required. 118 | 119 | Goroutines aren't free, but they're very cheap. 120 | 121 | More information about Go and concurrency 122 | .link https://youtu.be/f6kdp27TYZs?t=1 123 | 124 | 125 | * Java like BlockingQueue with Channels 126 | .code ../cp/channels/blockingqueue/blockingqueue.go /BlockingQueue/,/EOF/ 127 | 128 | * Java like BlockingQueue - Test 129 | .code ../cp/channels/blockingqueue/blockingqueue_test.go /TestBlockingQueue/,/EOF OMIT/ 130 | 131 | * Java like BlockingQueue with Locks (Low Level) 132 | .code ../cp/locks/blockingqueue/blockingqueue.go /BlockingQueue/,/A1/ 133 | 134 | * Java like BlockingQueue with Locks (Low Level) 135 | .code ../cp/locks/blockingqueue/blockingqueue.go /Put/,/A2/ 136 | 137 | 138 | * Dining Philosophers 139 | 140 | .image img/06-dining-philosophers.png 500 600 141 | 142 | 143 | * Dining Philosophers with Channels 144 | 145 | .image img/06-philosophers-channel.jpeg 600 800 146 | 147 | * Dining Philosophers - Hints 148 | - Never grab one fork and wait for the other. This is a deadlock situation. 149 | - If you cant get the second fork, you should immediately release the first one. 150 | - The table itself should be a Go Routine and return the forks to a requesting philosopher, this makes synchronization easy (the table is single threaded) 151 | - The philosopher loop looks like this: 152 | // Main loop 153 | func (p *Philosopher) run() { 154 | for { 155 | p.takeForks() 156 | p.eat() 157 | p.putForks() 158 | p.think() 159 | } 160 | } 161 | * Wrong Solutions 162 | There are many wrong solution on the web. 163 | Most of them share the problem that the Philosopher picks up the left fork (implemented with channels or locks) and immediately the right fork. 164 | The problem arises, when the second fork is in use. There is a potential deadlock, when all Philosophers wait on the second fork. 165 | In theory a deadlock occurs if there is a cycle in the Resource Allocation Graph. 166 | .link https://play.golang.org/p/rXCotNNY24 167 | 168 | * Summary 169 | - With Go you can solve sync problems with channels 170 | - Channels use Message Passing instead of locks 171 | - Go has a low level lock API, but this is seldom needed 172 | - It is possible to port all classes from java.util.locking easily -------------------------------------------------------------------------------- /docs/7.0-Distributed-Programming-Raft.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/7.0-Distributed-Programming-Raft.pdf -------------------------------------------------------------------------------- /docs/7.0-Distributed-Programming-Raft.slide: -------------------------------------------------------------------------------- 1 | Distributed Consensus and the Raft Algorithm 2 | Concepts of Programming Languages 3 | 21 Nov 2019 4 | Tags: go, programming, concurrent, go routines, channels 5 | 6 | Johannes Weigend 7 | Rosenheim Technical University 8 | johannes.weigend@qaware.de 9 | http://www.qaware.de 10 | 11 | * About this Lecture: Some Thoughts 12 | - Goal of this lecture is to look at a non trivial example in Go. 13 | - The problem we look at, is language agnostic. 14 | - It can be implemented in almost every language. 15 | - It combines concurrent programming and network programming. 16 | - It contains non trivial logic. 17 | - It is a real world example with high relevance. 18 | 19 | * What is Distributed Consensus? 20 | Distributed consensus is the problem how to achieve consistency in distributed systems. Distributed consensus protocols can be used for distributed databases which should stay consistent: 21 | 22 | - The system stays consistent even if some nodes goes down 23 | - The system stays consistent if a network partition occurs (but could get unavailable) 24 | - The system never responds with wrong or inconsistent data (different responses from nodes) 25 | 26 | * The CAP Theorem 27 | In a distributed system the following requirements can be met: 28 | 29 | - *Consistency* - Data is the same across the cluster, so you can read or write from/to any node and get the same data. 30 | - *Availability* - The system stays available even if one or more member goes down 31 | - *Partition* *Tolerance* - If parts of the system loose connection to other parts, the system stays alive 32 | 33 | Pick Two: Only two requirements can be met -> CP, AP are typical (CA does not exists) 34 | .link https://codahale.com/you-cant-sacrifice-partition-tolerance/ 35 | 36 | * Securing C and P 37 | - All known algorithms use the concept of a quorum to detect network partition problems 38 | - Only the partition with the majority of nodes stay alive to ensure consistency 39 | - All known algorithms use the concept of a Master or Leader node to control replication 40 | - All known algorithms use a replicated log and implement a two phase commit 41 | 42 | * Two Phase Commit 43 | Phases: 44 | 45 | - Prepare Phase - Data is persisted on all members. Data is not visible but stored. 46 | 47 | - Commit Phase - Persisted data will be loaded into memory. Data gets visible. 48 | 49 | - This is typically implemented with an append only log (transaction log) and a state machine which reads the log entries and loads them into memory. 50 | 51 | * What is Raft? 52 | - Raft is a protocol for distributed consensus 53 | - Raft is designed to be easy understandable 54 | - Raft predecessor Paxos was highly complex 55 | - Most Paxos implementations are buggy or academic 56 | - Raft was developed 2014 in a phd thesis at Stanford University 57 | - Zookeeper ZAB is an alternative but more complex solution 58 | .link https://raft.github.io/raft.pdf The Raft Paper 59 | .link https://github.com/lshmouse/reading-papers/blob/master/distributed-system/Zab:%20High-performance%20broadcast%20for%0Aprimary-backup%20systems.pdf The ZAB paper 60 | .link https://lamport.azurewebsites.net/pubs/lamport-paxos.pdf The Paxos Paper 61 | 62 | 63 | * Who uses Raft? 64 | - ectd - The Cloud Native key value store -> Part of Kubernetes! 65 | - Docker Swarm - Docker in cluster mode 66 | - dgraph - A Scalable, Distributed, Low Latency, High Throughput Graph Database 67 | - tikv - A Distributed transactional key value database powered by Rust and Raft 68 | - swarmkit - A toolkit for orchestrating distributed systems at any scale. 69 | - chain core - Software for operating permissioned, multi-asset blockchain networks 70 | - Elastic Search - Distributed Search engine (=document oriented database) 71 | - ... 72 | 73 | * The Raft Algorithm 74 | - Raft is based on a replicated state machine with the states: *FOLLOWER*, *CANDIDATE* and *LEADER* 75 | - All nodes start in the FOLLOWER state 76 | - The leader is responsible for sending heartbeat message to all members 77 | - The leader is responsible to replicate data to all other nodes (2 phase commit) 78 | - If a member does not receive a leader message in a random timeout interval, an election starts 79 | - During an election a candidate sends vote messages to all cluster members 80 | - The election is won, if a quorum (>50% = n/2 + 1) of members respond with OK 81 | 82 | * Raft in Action 83 | .link http://thesecretlivesofdata.com/raft/ Raft Explanation 84 | .link https://raft.github.io/raftscope/index.html The Raftscope Visualization 85 | 86 | * The Raft State Model 87 | - There is only one Leader which is responsible for consistency and replication 88 | .image https://3.bp.blogspot.com/-6hYgzWqZBVU/WoXoq9gg9LI/AAAAAAAAVH0/OojT-PYzg1kzYxBZ8Gqz2QUwkz-5-O_zACLcBGAs/s1600/Screen%2BShot%2B2018-02-15%2Bat%2B3.07.43%2BPM.png 89 | 90 | * Implementing Raft with Go 91 | The most prominent implementation is the Raft implementation of Etcd (Part of Kubernetes) 92 | .link https://github.com/etcd-io/etcd/tree/master/raft 93 | This implementation is highly optimized and abstracts from the Raft paper 94 | 95 | Other implementations 96 | .link https://github.com/hashicorp/raft 97 | .link https://github.com/cloudflare/go-raft 98 | .link https://github.com/peterbourgon/raft 99 | .link https://raft.github.io/raft.pdf Read the Raft paper for specification 100 | 101 | * Is it possible to build your own Raft implementation? 102 | Requirements and Decisions 103 | - We want to stay as close a possible on the original specification 104 | - We want to make a Raft cluster local testable (as single process) 105 | - We want to use gRPC as middleware 106 | .link https://github.com/jweigend/concepts-of-programming-languages/tree/master/dp/kvstore/core/raft 107 | 108 | * Step I - Defining a KV Business API 109 | .code ../dp/kvstore/kvstore-api.go 110 | 111 | - This is the business API for setting and getting data in/out or Raft cluster 112 | 113 | * Step II - The Raft Interface : AppendEntries 114 | .code ../dp/kvstore/core/raft/noderpc.go /type NodeRPC/ 115 | .code ../dp/kvstore/core/raft/noderpc.go /AppendEntries/,23 116 | - The interface could be easily proxied with gRPC or run locally without proxy 117 | 118 | * Step II - The Raft Interface : RequestVote 119 | .code ../dp/kvstore/core/raft/noderpc.go /type NodeRPC/ 120 | .code ../dp/kvstore/core/raft/noderpc.go /RequestVote/,37 121 | 122 | * Step III - Implement the Raft Interfaces in a Server/Node 123 | .code ../dp/kvstore/core/raft/node.go /type Node/,28 124 | 125 | * Step IV - Write Tests 126 | .code ../dp/kvstore/core/raft/node_test.go /TestElection/,33 127 | 128 | * Interesting Parts 129 | - Statemachine 130 | - Concurrency: Behavoir of remote calls, election and heartbeat timers 131 | - Design: Timer implementation 132 | - Clean Architecture: Separation of Raft and server APIs 133 | - Clean Code: Testability 134 | 135 | * Be Creative! 136 | - Write your own Raft Implementation! 137 | .link https://www.youtube.com/watch?v=YbZ3zDzDnrw More information 138 | 139 | * Summary 140 | - Go is an excellent choice to implement an distributed protocol like Raft 141 | - You can implement the Raft specification with ca 1000-2000 Loc 142 | - You can learn from the Etcd implementation, which is on Github 143 | - Building a production safe implementation is hard in any language anyway! -------------------------------------------------------------------------------- /docs/8.0-Systems-Programming.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/8.0-Systems-Programming.pdf -------------------------------------------------------------------------------- /docs/9.0-Enterprise-Programming-Modules.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/9.0-Enterprise-Programming-Modules.pdf -------------------------------------------------------------------------------- /docs/9.0-Enterprise-Programming-Modules.slide: -------------------------------------------------------------------------------- 1 | Large Scale Programming with Modules 2 | Concepts of Programming Languages 3 | 19 Dec 2019 4 | Tags: go, programming, modules 5 | 6 | Johannes Weigend (QAware GmbH) 7 | University of Applied Sciences Rosenheim 8 | johannes.weigend@qaware.de 9 | http://www.qaware.de 10 | @johannesweigend 11 | 12 | * Enterprise Programming 13 | - Large codebases 14 | - Conflicting dependencies 15 | - Stable dependencies 16 | - Compatiblity requirements 17 | 18 | * What is a Module? 19 | - A module is an unit of Programming, Testing, Distribution, Versioning and Visibility 20 | - Languages have diffent interpretations and focus 21 | - Decomposition into modules is a well understood 22 | 23 | "The major advancement in the area of modular 24 | programming has been the development of coding 25 | techniques and assemblers which (l) allow one module 26 | to be written with little knowledge of the code in 27 | another module, and (2) allow modules to be reassembled 28 | and replaced without reassembly of the whole 29 | system." 30 | .link https://www.win.tue.nl/~wstomv/edu/2ip30/references/criteria_for_modularization.pdf On the Criteria To Be Used in Decomposing Systems into Modules, David Parnas 1972 31 | 32 | * Visibility 33 | - Makes sure that certain content of a module is private and can not be accessed be other modules 34 | - The benefits are the same like OO encapsulation but at larger scale 35 | - Other modules can only depend on the public (API) part of a module 36 | - Modules hide complexity from clients (information hiding) 37 | 38 | * Distribution and Replaceability 39 | - Modules are monolithic deployment units 40 | - Modules could be separate developed and released 41 | - Languages differ in the way an application is assembled from modules (static or dynamic linking) 42 | - Modules can be exchanged without affecting the whole system 43 | 44 | * Versioning 45 | - Modules should be versionized to guarantee stable dependencies 46 | - The version should make the module immutable, so a module can be clearly identified by its version 47 | - Languages differ greatly on that issue (From support of naming conventions to build tools (maven)) 48 | 49 | * Versions and Conflicts 50 | .image img/09-conflicting-versions.png 500 800 51 | 52 | * Module System in Java 53 | - Java knows Modules since Java SE 9 (Project Jigsaw) 54 | - Java Modules focus on Distribution and Visibility but not on Versioning! 55 | - Java need 3rd party tools to implement versionized dependencies (Maven, Gradle ...) 56 | .link https://www.sigs-datacom.de/uploads/tx_dmjournals/weigend_JS_05_16_6n6J.pdf 57 | 58 | * The Go Module System 59 | - There is an evolution in the Go module systems 60 | - Before 1.11: Go dep was the tool of choice to implement safe, versionized dependencies 61 | - Since 1.11: Go modules is the concept for the future 62 | .link https://github.com/golang/go/wiki/Modules The Go Module System 63 | 64 | * The Principles of Versioning in Go 65 | *Repeatability* 66 | Make sure that build results never change over time !!! 67 | 68 | *Compatibility* 69 | Make sure that released code stays compatible. Make incompatible changes explicit visible to users of your code !!! 70 | 71 | *Cooperation* 72 | Make sure your code ist stable with the latest minor version of a dependent library 73 | 74 | * Semantic Versioning 75 | - Distinguish MAJOR, MINOR and PATCH version numbers 76 | - A version number is of the form: .. (e.g 1.2.0, 0.9.0-alpha) 77 | - Semantic versioning defines several rules how and when to change the version number 78 | .link https://semver.org/ 79 | 80 | * The Design of the Go Module System 81 | Russ Cox (@_rsc, rsc@swtch.com) 82 | c/o Google 83 | 5 Cambridge Center 84 | Cambridge, MA 02142 85 | .link https://www.youtube.com/watch?v=F8nrpe0XWRg 86 | 87 | * The Go Module System in Action 88 | .image http://www.futureanalytics.ie/wp-content/uploads/2016/12/Modules-i3.jpg 89 | .link https://youtu.be/V8FQNen4WAA 90 | .link https://youtu.be/aeF3l-zmPsY 91 | 92 | * Hands On 93 | - Write a Go module mail with API and impl 94 | - The Mail implementation should log a message with logrus (https://github.com/sirupsen/logrus) 95 | - Change your module dependency to a former logrus version 96 | - Write a second Go module client which uses the mail module 97 | - Make a incompatible change to your mail API and increase the major version number 98 | 99 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # How to use the Slides 2 | These slide files (.slide) are intended to be viewed with the 'present' tool. 3 | Use go get to install 'present.' 4 | 5 | $ go get code.google.com/p/go.talks/present 6 | 7 | You can start the slideshow by running the present tool 8 | 9 | $ present 10 | 11 | This will start a webserver. The current directory and all subdirs are served. 12 | 13 | Or use the provided Makefile: 14 | 15 | $ make slideshow 16 | 17 | This will also start the present tool. 18 | -------------------------------------------------------------------------------- /docs/exercises/Exercise1.md: -------------------------------------------------------------------------------- 1 | # Exercise 1 - Getting Started with Go 2 | 3 | If you do not finish during the lecture period, please finish it as homework. 4 | 5 | ## Setup 6 | 7 | - Install Go from inside a virtual disk 8 | (.vhdx on Windows or .sparseimage on Mac) in a /software/go subdirectory. 9 | 10 | - Win: 11 | - Mac: 12 | 13 | - Create a Go workspace on the disk in the <>/codebase/gopath directory 14 | - 15 | - Create a shell script (.sh / .cmd) to make your changes to the `GOPATH` and PATH environment variables persistent. 16 | - Create a Github project with your personal account containing a `HelloWorld.go` program 17 | - Use go get github.com/\<\\> to copy the repository into your local `GOPATH`. 18 | - Add `HelloWorld.go` to the checkout path and `commit / push` the file. 19 | - Test the `HelloWorld` program with "go run HelloWorld" 20 | - Optional: Install Visual Studio Code, IntelliJ or any other Editor with Go support inside your virtual disk. 21 | 22 | ## After this Exercise 23 | 24 | - You should know how to compile and run Go code 25 | - You should know about the meaning of the `GOPATH` and PATH variables 26 | - You should have a portable Go installation inside a separate disk or directory on your computer 27 | - Your personal Github project is cloned into your workspace. You are able to add, remove and commit files. 28 | -------------------------------------------------------------------------------- /docs/exercises/Exercise2.1.md: -------------------------------------------------------------------------------- 1 | # Exercise 2.1 - Basics 2 | 3 | If you do not finish during the lecture period, please finish it as homework. 4 | 5 | ## Pointers (swap.go) 6 | 7 | - Write a function to swap two integer variables in the caller function (2 ways) 8 | - Write a function to swap two integer pointers in the caller function (2 ways) 9 | - Write test example code "ExampleSwap inside a swap_test file 10 | 11 | ### Questions 12 | 13 | What is the difference between a pointer and a Java object reference? 14 | What happens if you assign nil to a pointer variable and dereference the pointer? 15 | 16 | ## Book Index (Working with maps, arrays and slices) 17 | 18 | A Book Index is a inverted index which lists all pages a word occurs in a book. 19 | Write a program which generates an inverted index out of an array of book pages. 20 | Each Page contains an array of words. 21 | 22 | - Define custom types for Book, Page and Index 23 | - Make sure the Stringer() interface is implemented for Book and Index to make them printable 24 | - Write an unit test which generates a book and calculates the index and prints it to Stdout 25 | 26 | ## After this Exercise 27 | 28 | - You know difference between Pointers and Values 29 | - You know the basic Go container types: string, map, array, slice 30 | - You know how to make custom types printable 31 | - You know how to write basic unit tests 32 | -------------------------------------------------------------------------------- /docs/exercises/Exercise2.2.md: -------------------------------------------------------------------------------- 1 | # Exercise 2.2 - Basic OOP 2 | 3 | If you do not finish during the lecture period, please finish it as homework. 4 | 5 | ## Rational Numbers (Datatypes) 6 | 7 | Write a type for rational numbers (a/b). Implement a constructor, methods for adding and multiplying rational numbers. 8 | 9 | - Implement the euclidean algorithm for reducing rational numbers. Make sure the == and != operators work correct for 1/2 and 2/4 10 | - Write a constructor function to create rational numbers 11 | - The type should be immutable 12 | - Make sure to implement the Stringer() interface 13 | - Write an unit test to test the rational numbers 14 | - Check the test coverage. It should be greater than 80% 15 | 16 | ### Questions 17 | 18 | - What type has the receiver (pointer or value). Why? 19 | - What does immutable mean? Why is it good design to make Rational immutable? 20 | 21 | ## Stack (Containers) 22 | 23 | Write a generic LIFO container (stack) for all types. The stack should have at least four methods: 24 | 25 | - Push(object) 26 | - Pop() 27 | - Size() 28 | - GetAt(index) 29 | - Write a unit test which uses build in types and object types (Rational) inside the stack. Calculate the sum of all elements in the stack. 30 | 31 | ## After this Exercise 32 | 33 | - You should know how to write OO code in Go 34 | - You should how to reference other packages in your codebase 35 | - You should know the difference between object and data types 36 | - You should know how generic containers are built with Go 37 | -------------------------------------------------------------------------------- /docs/exercises/Exercise3.md: -------------------------------------------------------------------------------- 1 | # Exercise 3 - OOP in Go 2 | 3 | If you do not finish during the lecture period, please finish it as homework. 4 | 5 | ## Exercise 3.1 - Interfaces, Polymorphism and Embedding 6 | 7 | The image shows a typical UML design with inheritance, aggregation and polymorph methods. 8 | 9 | ![oo](../img/03-exercise.png "A typical OO design") 10 | 11 | Implement this design as close as possible to the design in Go: 12 | 13 | - The `Paint()` method should print the names and values of the fields to the console 14 | - Allocate an array of polymorph objects and call Paint() in a loop 15 | 16 | ## Exercise 3.2 - Mail Component and Service Locator 17 | 18 | Implement the following interface: 19 | 20 | ```go 21 | type Sender interface { 22 | 23 | // Send an email to a given address with a message. 24 | SendMail(address Address, message string) 25 | } 26 | ``` 27 | 28 | Implement the interface and write a client. The implementation should be provided by 29 | a simple self made service locator registry: 30 | 31 | ```go 32 | // Create an implementation for the mail.Sender interface 33 | var sender = Registry.Get("mail.Sender").(mail.Sender) 34 | 35 | mailaddrs := mail.Address{Address:"test@test.com"} 36 | sender.SendMail(mailaddrs, message) 37 | ``` 38 | 39 | Interface, client and implementation should be in separate packages. 40 | -------------------------------------------------------------------------------- /docs/exercises/Exercise4.md: -------------------------------------------------------------------------------- 1 | # Exercise 4 - Building Parsers 2 | 3 | During the course you learned about grammar, lexers and parsers. 4 | The exercise is split up into three parts: 5 | 6 | 1) Implement a lexer to extract tokens from a boolean expression 7 | 2) Implement a parser to build an Abstract Syntax Tree and to evaluate the expression 8 | 3) Implement both the lexer and the parser using Antlr 9 | 10 | If you do not finish during the lecture period, please try this at home. 11 | 12 | ## Exercise 4.1 - Lexer for boolean expressions 13 | 14 | Write a lexer for boolean expressions inclusive braces. 15 | Use the following grammar definition: 16 | 17 | ```bnf 18 | ::= { } 19 | ::= { } 20 | ::= | | () 21 | ::= '|' 22 | ::= '&' 23 | ::= '!' 24 | ::= '[a-zA-Z0-9]*' 25 | ``` 26 | 27 | _Note:_ A lexer only uses the lexer rules from the grammar. 28 | 29 | The lexer has a method 30 | 31 | ```go 32 | func splitTokens(input string) []string { 33 | ``` 34 | 35 | to split the expression into tokens and a further method 36 | 37 | ```go 38 | func (l *Lexer) NextToken() string 39 | ``` 40 | 41 | that iterates over the tokens. 42 | 43 | **Disclaimer:** Feel free you use your very own software design. 44 | 45 | 🤥 **Write tests! Otherwise it does not happened!** 🤥 46 | 47 | ## Exercise 4.2 - Parser 48 | 49 | The parser is split up into two parts: 50 | 51 | - Define and implement an Abstract Syntax Tree 52 | - Token parsing and Abstract Syntax Tree building 53 | 54 | _Reminder:_ Use the following grammar definition: 55 | 56 | ```bnf 57 | ::= { } 58 | ::= { } 59 | ::= | | () 60 | ::= '|' 61 | ::= '&' 62 | ::= '!' 63 | ::= '[a-zA-Z0-9]*' 64 | ``` 65 | 66 | ### Exercise 4.2.1 - Abstract Syntax Tree (AST) 67 | 68 | Write a program which builds an AST with nodes to evaluate logical expressions with (And, Not, Or with variables). 69 | 70 | ```text 71 | Sample Expression: `A AND B OR C` 72 | 73 | ---------- 74 | | OR | 75 | ---------- 76 | / \ 77 | --------- ---------- 78 | | AND | | Var:C | 79 | --------- ---------- 80 | / \ 81 | --------- --------- 82 | | Var:A | | Var:B | 83 | --------- --------- 84 | ``` 85 | 86 | The tree should be evaluated with a evaluation methods which supports named variables: 87 | 88 | ```go 89 | eval(vars map[string]bool) bool 90 | ``` 91 | 92 | _Why named variables:_ This allows us to build the AST once and use it for multiple variable values. 93 | 94 | Notes that might help: 95 | 96 | - Interfaces and Polymorphism 97 | - Nodes are different but what are the commonalities? 98 | - Simply follow the rules 99 | 100 | 🤥 **Write tests! Otherwise it does not happened!** 🤥 101 | 102 | Write a unit test which builds the AST and evaluate the expression with given boolean values for the variables A, B, C. 103 | 104 | ### Exercise 4.2.2 Recursive Descent Parser 105 | 106 | Write a recursive descent parser. The parser must implement the grammar rules (that was enough hint). 107 | 108 | 🤥 **Write tests! Otherwise it does not happened!** 🤥 109 | 110 | ### Exercise 4.3 Antlr 111 | 112 | We now use Antlr to generate a lexer and a parser for a given grammar definition. 113 | 114 | Follow the go Antlr quick-start: 115 | 116 | You need to do the following things: 117 | 118 | - Antlr Setup (see above) 119 | - Define an Antlr grammar file (`boolparser.g4`) 120 | - Generate lexer and parser source code 121 | - Use the generated files to parse boolean expressions 122 | 123 | Should be not to hard 🤙 124 | -------------------------------------------------------------------------------- /docs/exercises/Exercise5.md: -------------------------------------------------------------------------------- 1 | # Exercise 5 - Functional Programming in Go 2 | 3 | If you do not finish during the lecture period, please finish it as homework. 4 | 5 | ## Exercise 5.1 - Warm Up 6 | 7 | Write a Go Programm which shows the following concepts: 8 | 9 | - Functions as Variables 10 | - Anonymous Lambda Functions 11 | - High Order Functions (functions as parameters or return values) 12 | - Clojures (https://en.wikipedia.org/wiki/Closure_(computer_programming)) 13 | 14 | Compare the Syntax with Java and discuss this in a group of students. 15 | 16 | ## Exercise 5.2 - Functional Composition (g◦f)(x) 17 | 18 | Write a Go function to compose two *unknown* unary functions (one argument and one return value). The functions to compose should be arguments. 19 | Write a Unit Test for that function. 20 | 21 | ## Exercise 5.3 - Map / Filter / Reduce 22 | Map/Reduce is a famous functional construct implemented in many parallel and distributed collection frameworks like 23 | Hadoop, Apache Spark, Java Streams (not distributed but parallel), C# Linq 24 | 25 | - Implement a custom M/R API with the following interface: 26 | ```go 27 | type Stream interface { 28 | Map(m Mapper) Stream 29 | Filter(p Predicate) Stream 30 | Reduce(a Accumulator) Any 31 | } 32 | ``` 33 | The usage of the interface should be like this: 34 | ```go 35 | stringSlice := []Any{"a", "b", "c", "1", "D"} 36 | 37 | // Map/Reduce 38 | result := ToStream(stringSlice). 39 | Map(toUpperCase). 40 | Filter(notDigit). 41 | Reduce(concat).(string) 42 | 43 | if result != "A,B,C,D" { 44 | t.Error(fmt.Sprintf("Result should be 'A,B,C,D' but is: %v", result)) 45 | } 46 | ``` 47 | 48 | *Questions* 49 | - What is the type of Mapper, Predicate and Accumulator? 50 | - How can you make the types generic, so they work for any type, not only for string? 51 | 52 | ## Exercise 5.4 - Word Count (WC) 53 | Word Count is a famous algorithm for demonstrating the power of distributed collections and functional programming. 54 | Word Count counts how often a word (string) occurs in a collection. It is easy to address that problem with shared state (a map), but 55 | this solution does not scale well. 56 | The question here is how to use a pure functional algorithm to enable parallel and distributed execution. 57 | 58 | After running Word Count, you should get the following result: 59 | 60 | INPUT: "A" "B" "C" "A" "B" "A" 61 | 62 | OUTPUT: ("A":3) ("B":2) ("C":1) 63 | 64 | *Questions* 65 | - How can you implement the problem with the already built Map()/Filter()/Reduce() functions? 66 | - Write an Unit Test to prove that your solution works as expected! 67 | -------------------------------------------------------------------------------- /docs/exercises/Exercise6.md: -------------------------------------------------------------------------------- 1 | # Exercise 6 - Concurrent Programming in Go 2 | 3 | If you do not finish during the lecture period, please finish it as homework. 4 | 5 | ## Exercise 6.1 - Warm Up 6 | 7 | Test the Ping-Pong Programm (see slides) 8 | 9 | ## Exercise 6.2 - Fan-Out / Fan-In 10 | 11 | Write tests for the FanOut and FanIn functions (see slides) 12 | 13 | ## Exercise 6.3 - Dining Philosophers 14 | 15 | Write a program to simulate the Dining Philosophers Problem by using Go Channels. 16 | - There should be one Go Routine for each Philosopher and the Table 17 | - Make sure that: 18 | - The distribution of forks is fair - No philosopher dies on starvation 19 | - Use the given Unit Test: 20 | 21 | ```go 22 | func TestPhilosophers(t *testing.T) { 23 | 24 | var COUNT = 5 25 | 26 | // start table for 5 philosophers 27 | table := NewTable(COUNT) 28 | 29 | // create 5 philosophers and run parallel 30 | for i := 0; i < COUNT; i++ { 31 | philosopher := NewPhilosopher(i, table) 32 | go philosopher.run() 33 | } 34 | go table.run() 35 | 36 | // simulate 10 milliseconds --> check output 37 | time.Sleep(10 * time.Millisecond) 38 | } 39 | ``` 40 | 41 | Sample console output: 42 | 43 | ```sh 44 | [->]: Philosopher #0 eats ... 45 | [->]: Philosopher #3 eats ... 46 | [<-]: Philosopher #0 eat ends. 47 | [<-]: Philosopher #3 eat ends. 48 | [->]: Philosopher #0 thinks ... 49 | [->]: Philosopher #1 eats ... 50 | [->]: Philosopher #3 thinks ... 51 | [->]: Philosopher #4 eats ... 52 | [<-]: Philosopher #1 eat ends. 53 | [->]: Philosopher #1 thinks ... 54 | [<-]: Philosopher #4 eat ends. 55 | [->]: Philosopher #2 eats ... 56 | [->]: Philosopher #4 thinks ... 57 | [<-]: Philosopher #0 thinking ends 58 | [->]: Philosopher #0 eats ... 59 | [<-]: Philosopher #3 thinking ends 60 | ``` -------------------------------------------------------------------------------- /docs/img/00-programming-languages.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/00-programming-languages.jpeg -------------------------------------------------------------------------------- /docs/img/01-cncf-projects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/01-cncf-projects.png -------------------------------------------------------------------------------- /docs/img/01-exercise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/01-exercise.png -------------------------------------------------------------------------------- /docs/img/01-go-programming-language.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/01-go-programming-language.png -------------------------------------------------------------------------------- /docs/img/01-languages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/01-languages.png -------------------------------------------------------------------------------- /docs/img/02-exercise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/02-exercise.png -------------------------------------------------------------------------------- /docs/img/03-exercise.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/03-exercise.key -------------------------------------------------------------------------------- /docs/img/03-exercise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/03-exercise.png -------------------------------------------------------------------------------- /docs/img/04-lambda.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/04-lambda.key -------------------------------------------------------------------------------- /docs/img/04-lambda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/04-lambda.png -------------------------------------------------------------------------------- /docs/img/04-parsers/01-practice-go.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/04-parsers/01-practice-go.gif -------------------------------------------------------------------------------- /docs/img/04-parsers/02-menti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/04-parsers/02-menti.png -------------------------------------------------------------------------------- /docs/img/04-parsers/02-parsers-for-what.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/04-parsers/02-parsers-for-what.png -------------------------------------------------------------------------------- /docs/img/04-parsers/03-parsers-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/04-parsers/03-parsers-overview.png -------------------------------------------------------------------------------- /docs/img/04-parsers/04-ast-vs-cst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/04-parsers/04-ast-vs-cst.png -------------------------------------------------------------------------------- /docs/img/04-parsers/05-tree-terms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/04-parsers/05-tree-terms.png -------------------------------------------------------------------------------- /docs/img/04-parsers/05-whaat.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/04-parsers/05-whaat.gif -------------------------------------------------------------------------------- /docs/img/04-parsers/Parsers.006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/04-parsers/Parsers.006.png -------------------------------------------------------------------------------- /docs/img/04-parsers/Parsers.007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/04-parsers/Parsers.007.png -------------------------------------------------------------------------------- /docs/img/04-parsers/Parsers.008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/04-parsers/Parsers.008.png -------------------------------------------------------------------------------- /docs/img/04-parsers/Parsers.009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/04-parsers/Parsers.009.png -------------------------------------------------------------------------------- /docs/img/04-parsers/Parsers.010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/04-parsers/Parsers.010.png -------------------------------------------------------------------------------- /docs/img/04-parsers/Parsers.011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/04-parsers/Parsers.011.png -------------------------------------------------------------------------------- /docs/img/04-parsers/programming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/04-parsers/programming.png -------------------------------------------------------------------------------- /docs/img/05-once-or-more.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | A 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /docs/img/05-optional.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | A 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /docs/img/05-repetition.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | A 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /docs/img/06-dining-philosophers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/06-dining-philosophers.png -------------------------------------------------------------------------------- /docs/img/06-go-concurrency.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/06-go-concurrency.jpeg -------------------------------------------------------------------------------- /docs/img/06-moores-law.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/06-moores-law.png -------------------------------------------------------------------------------- /docs/img/06-philosophers-channel.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/06-philosophers-channel.jpeg -------------------------------------------------------------------------------- /docs/img/06-philosophers-channel.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/06-philosophers-channel.key -------------------------------------------------------------------------------- /docs/img/07-idserv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/07-idserv.png -------------------------------------------------------------------------------- /docs/img/07-proxy-pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/07-proxy-pattern.png -------------------------------------------------------------------------------- /docs/img/09-conflicting-versions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/09-conflicting-versions.png -------------------------------------------------------------------------------- /docs/img/go.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/img/go.png -------------------------------------------------------------------------------- /docs/snippets/cp: -------------------------------------------------------------------------------- 1 | f("hello", "world") // f runs; we wait 2 | 3 | go f("hello", "world") // f starts running 4 | g() // does not wait for f to return 5 | 6 | timerChan := make(chan time.Time) 7 | go func() { 8 | time.Sleep(deltaT) 9 | timerChan <- time.Now() // send time on timerChan 10 | }() 11 | 12 | // Do something else; when ready, receive. 13 | // Receive will block until timerChan delivers. 14 | // Value sent is other goroutine's completion time. 15 | completedAt := <-timerChan 16 | 17 | select { 18 | case v := <-ch1: 19 | fmt.Println("channel 1 sends", v) 20 | case v := <-ch2: 21 | fmt.Println("channel 2 sends", v) 22 | default: // optional 23 | fmt.Println("neither channel was ready") 24 | } 25 | 26 | func Query(conns []Conn, query string) Result { 27 | ch := make(chan Result, len(conns)) // buffered 28 | for _, conn := range conns { 29 | go func(c Conn) { 30 | ch <- c.DoQuery(query): 31 | }(conn) 32 | } 33 | return <-ch 34 | } 35 | 36 | func XQuery(conns []Conn, query string) Result { 37 | ch := make(chan Result, 1) // buffer of 1 item 38 | for _, conn := range conns { 39 | go func(c Conn) { 40 | select { 41 | case ch <- c.DoQuery(query): 42 | // nothing to do 43 | default: // executes if ch is blocked 44 | // nothing to do 45 | } 46 | }(conn) 47 | } 48 | return <-ch 49 | } 50 | 51 | 52 | func Compose(f, g func(x float) float) 53 | func(x float) float { 54 | return func(x float) float { 55 | return f(g(x)) 56 | } 57 | } 58 | 59 | print(Compose(sin, cos)(0.5)) 60 | 61 | go func() { // copy input to output 62 | for val := range input { 63 | output <- val 64 | } 65 | }() 66 | -------------------------------------------------------------------------------- /docs/studywork/Study-Work-Presentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/studywork/Study-Work-Presentation.pdf -------------------------------------------------------------------------------- /docs/studywork/Study-Work-Presentation.slide: -------------------------------------------------------------------------------- 1 | Semester Work 2 | Concepts of Programming Languages 3 | 12 Dec 2019 4 | Tags: go, programming, concurrent, go routines, channels 5 | 6 | Johannes Weigend 7 | Rosenheim Technical University 8 | johannes.weigend@qaware.de 9 | http://www.qaware.de 10 | 11 | * Semester Work 12 | - 15 Minutes presentation (in German) 13 | - 15.000 - 20.000 character Asciidoc document (in English) 14 | - Sourcecode + Unit Tests + Buildscript on Github (Tagged Version required) 15 | - Deadline - 01-08-2020 - EOB - Delivery per Mail to johannes.weigend@qaware.de 16 | - Where: QAware Office Rosenheim, Brückenstr. 1, 2.OG 17 | 18 | * Timeline - 01-09-2020 19 | 9:45 - 13:15 - Functional Boolparser + Single Process Raft 20 | 21 | - Compare Functional Programming. Go with *F#*: Victor Wolf 22 | - Compare Functional Programming. Go with *Haskell*: Veronika Holzmayer 23 | - Compare Functional Programming. Go with *JavaScript*: Markus Voit 24 | - Compare Functional Programming. Go with *Objective* *C*: Simon Treutlein 25 | 26 | - Compare Parallel Programming. Go with *Python*: Lukas Kiederle 27 | - Compare Parallel Programming. Go with *Scala*: Max Bundscherer 28 | 29 | * Timeline - 01-16-2020 30 | 9:45 - 12:00 - OOP Boolparser + Distributed Raft Microservice 31 | 32 | - Compare Object Oriented Programming. Go with *Smalltalk*: Markus Kaleta 33 | - Compare Object Oriented Programming. Go with *TypeScript*: Alexander Hauenstein 34 | - Compare Object Oriented Programming. Go with *Ruby*: Alexander Hennecke 35 | 36 | - Compare Distributed Programming. Go with *Kotlin*: Tobias Lautenschlager 37 | -------------------------------------------------------------------------------- /docs/studywork/StudyWork.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/studywork/StudyWork.xlsx -------------------------------------------------------------------------------- /docs/talks/raft/Implementing Raft.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/docs/talks/raft/Implementing Raft.pptx -------------------------------------------------------------------------------- /dp/idserv/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile. Usage: make . 3 | # 4 | 5 | 6 | help: 7 | clear 8 | @echo "---------------------------------------------------------------------------------------------------------" 9 | @echo "Usage: make " 10 | @echo "---------------------------------------------------------------------------------------------------------" 11 | @echo "Valid targets are:" 12 | @echo " - ginstall : Installs / Updates the GRPC Go Libraries " 13 | @echo " - gcompile : Compiles the .pb protocol files" 14 | @echo " - run-server : Compiles and starts the server" 15 | @echo " - run-client : Compiles and runs the client" 16 | @echo " - clean : Clean up generated files" 17 | @echo "---------------------------------------------------------------------------------------------------------" 18 | 19 | ginstall: 20 | go get -u google.golang.org/grpc 21 | go get -u github.com/golang/protobuf/protoc-gen-go 22 | echo Make sure that protoc is installed for your platform 23 | 24 | gcompile: 25 | protoc -I remote/idserv/ remote/idserv/idserv.proto --go_out=plugins=grpc:remote/idserv 26 | 27 | clean: 28 | rm remote/idserv/*.pb.go 29 | 30 | run-server: 31 | go run remote/server/main.go 32 | 33 | run-client: 34 | go run client/main.go 35 | 36 | -------------------------------------------------------------------------------- /dp/idserv/client/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | package main 4 | 5 | import ( 6 | "log" 7 | 8 | "github.com/jweigend/concepts-of-programming-languages/dp/idserv" 9 | "github.com/jweigend/concepts-of-programming-languages/dp/idserv/core" 10 | "github.com/jweigend/concepts-of-programming-languages/dp/idserv/remote/proxy" 11 | ) 12 | 13 | // GenerateIds calls n-times NewUUID() in a loop and returns the result as slice. 14 | func GenerateIds(count int, service idserv.IDService) []string { 15 | result := make([]string, count) 16 | for i := 0; i < count; i++ { 17 | result[i] = service.NewUUID("c1") 18 | } 19 | return result 20 | } 21 | 22 | func main() { 23 | var service idserv.IDService 24 | 25 | // Local 26 | service = core.NewIDServiceImpl() 27 | result := GenerateIds(10, service) 28 | 29 | log.Printf("Got Id: %v", result) 30 | 31 | // Remote 32 | service = proxy.NewProxy() 33 | result = GenerateIds(10, service) 34 | 35 | log.Printf("Got Id: %v", result) 36 | } 37 | -------------------------------------------------------------------------------- /dp/idserv/core/idserv-impl.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package impl contains the business logic of the IDService 5 | package core 6 | 7 | import ( 8 | "fmt" 9 | "sync/atomic" 10 | ) 11 | 12 | // IDServiceImpl type 13 | type IDServiceImpl struct { 14 | } 15 | 16 | // The last given Id. 17 | var lastID int64 18 | 19 | // NewIDServiceImpl creates a new instance 20 | func NewIDServiceImpl() *IDServiceImpl { 21 | return new(IDServiceImpl) 22 | } 23 | 24 | // NewUUID implements the IDService interface. 25 | func (ids *IDServiceImpl) NewUUID(clientID string) string { 26 | result := atomic.AddInt64(&lastID, 1) 27 | return fmt.Sprintf("%v:%v", clientID, result) 28 | } 29 | -------------------------------------------------------------------------------- /dp/idserv/idserv-api.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package idserv contains the IDService API. 5 | package idserv 6 | 7 | // IDService can be used to produce network wide unique ids. 8 | type IDService interface { 9 | 10 | // NewUUID generates an UUID with a given client prefix. 11 | NewUUID(clientID string) string 12 | } 13 | -------------------------------------------------------------------------------- /dp/idserv/remote/idserv/idserv.proto: -------------------------------------------------------------------------------- 1 | 2 | syntax = "proto3"; 3 | 4 | package idserv; 5 | 6 | // The IDService definition 7 | service IDService { 8 | // NewUUID generates a globally unique ID 9 | rpc NewUUID (IdRequest) returns (IdReply) {} 10 | } 11 | 12 | // The client sends a unique id. 13 | message IdRequest { 14 | string clientId = 1; 15 | } 16 | 17 | // The response message contains the uuid. 18 | message IdReply { 19 | string uuid = 1; 20 | } -------------------------------------------------------------------------------- /dp/idserv/remote/proxy/proxy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package proxy contains the client side proxy. 5 | package proxy 6 | 7 | import ( 8 | "context" 9 | "fmt" 10 | "log" 11 | "time" 12 | 13 | "github.com/jweigend/concepts-of-programming-languages/dp/idserv/remote/idserv" 14 | "google.golang.org/grpc" 15 | ) 16 | 17 | const ( 18 | address = "localhost:50051" 19 | defaultName = "client" 20 | ) 21 | 22 | // Proxy is a client side proxy which encapsulates the RPC logic. It implements the IDService interface. 23 | type Proxy struct { 24 | connection *grpc.ClientConn 25 | } 26 | 27 | // NewProxy creates a Proxy and starts the server connection 28 | func NewProxy() *Proxy { 29 | p := new(Proxy) 30 | conn, err := grpc.Dial(address, grpc.WithInsecure()) 31 | if err != nil { 32 | panic(fmt.Sprintf("did not connect: %v", err)) 33 | } 34 | p.connection = conn 35 | return p 36 | } 37 | 38 | // NewUUID implements the IDService interface. 39 | func (p *Proxy) NewUUID(clientID string) string { 40 | c := idserv.NewIDServiceClient(p.connection) 41 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 42 | defer cancel() 43 | r, err := c.NewUUID(ctx, &idserv.IdRequest{ClientId: clientID}) 44 | if err != nil { 45 | log.Printf("could not generate id: %v", err) 46 | r.Uuid = "" 47 | } 48 | return r.Uuid 49 | } 50 | -------------------------------------------------------------------------------- /dp/idserv/remote/server/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | package main 4 | 5 | import ( 6 | "log" 7 | "net" 8 | 9 | "github.com/jweigend/concepts-of-programming-languages/dp/idserv/remote/idserv" 10 | "github.com/jweigend/concepts-of-programming-languages/dp/idserv/remote/stub" 11 | "google.golang.org/grpc" 12 | "google.golang.org/grpc/reflection" 13 | ) 14 | 15 | const ( 16 | port = ":50051" 17 | ) 18 | 19 | func main() { 20 | lis, err := net.Listen("tcp", port) 21 | if err != nil { 22 | log.Fatalf("failed to listen: %v", err) 23 | } 24 | s := grpc.NewServer() 25 | idserv.RegisterIDServiceServer(s, &stub.Server{}) 26 | // Register reflection service on gRPC server. 27 | reflection.Register(s) 28 | if err := s.Serve(lis); err != nil { 29 | log.Fatalf("failed to serve: %v", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /dp/idserv/remote/stub/idserv-stub.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | package stub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/jweigend/concepts-of-programming-languages/dp/idserv/core" 10 | "github.com/jweigend/concepts-of-programming-languages/dp/idserv/remote/idserv" 11 | ) 12 | 13 | // Server is used to implement idserv.IdServer 14 | type Server struct{} 15 | 16 | // NewUUID implements idserv.IdService interface 17 | func (s *Server) NewUUID(c context.Context, r *idserv.IdRequest) (*idserv.IdReply, error) { 18 | service := core.IDServiceImpl{} 19 | return &idserv.IdReply{Uuid: service.NewUUID(r.GetClientId())}, nil 20 | } 21 | -------------------------------------------------------------------------------- /dp/kvstore/Makefile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jweigend/concepts-of-programming-languages/68fb6c7920ac98aa4574630149d5bf915eb5197a/dp/kvstore/Makefile -------------------------------------------------------------------------------- /dp/kvstore/README.md: -------------------------------------------------------------------------------- 1 | # DKVS 2 | ## A Distributed Key Value Store 3 | 4 | KV-Store is a simple, non-persistent distributed Key Value Store with a REST- and GRPC API. 5 | 6 | ```sh 7 | // Copyright 2018 Johannes Weigend 8 | // 9 | // Licensed under the Apache License, Version 2.0 (the "License"); 10 | // you may not use this file except in compliance with the License. 11 | // You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | // Unless required by applicable law or agreed to in writing, software 16 | // distributed under the License is distributed on an "AS IS" BASIS, 17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | ``` 21 | 22 | The consistency of data is guaranteed by using the RAFT Algorithm: 23 | 24 | http://thesecretlivesofdata.com/raft/ 25 | https://raft.github.io/ 26 | 27 | -------------------------------------------------------------------------------- /dp/kvstore/core/kvstore-impl.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | // KVStore is a distributed key value store using RAFT consensus. 4 | type KVStore struct { 5 | committedData map[string]string 6 | } 7 | 8 | // NewKVStore constructs a new KVStore. 9 | func NewKVStore() *KVStore { 10 | return new(KVStore) 11 | } 12 | 13 | // SetString sets a value for a given key. 14 | func (k *KVStore) SetString(key, value string) { 15 | // Make the new value visible (async) when the majority of cluster members have written it to the logfile. 16 | go func() { 17 | // success - more than 50% of all cluster member have written the value to their logs 18 | k.committedData[key] = value 19 | }() 20 | } 21 | 22 | // GetString returns the value for a given key. 23 | func (k *KVStore) GetString(key string) string { 24 | return k.committedData[key] 25 | } 26 | -------------------------------------------------------------------------------- /dp/kvstore/core/raft/cluster.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package raft is an implementation of the RAFT consensus algorithm. 5 | package raft 6 | 7 | import "errors" 8 | 9 | // Cluster knows the RPC interface of all members. 10 | // The cluster works with multiple nodes for testing or with RPC proxies for remote access in a distributed scenario. 11 | type Cluster struct { 12 | allNodes []*Node 13 | } 14 | 15 | // NewCluster constructs a new cluster with all Node RPC interfaces. 16 | // For local test the NodeRPC is the Node itself. For distributed operation the NodeRPC is a proxy. 17 | func NewCluster(allNodes []*Node) *Cluster { 18 | return &Cluster{allNodes} 19 | } 20 | 21 | // GetRemoteFollowers return the NodeRPC If of all nodes. 22 | func (c *Cluster) GetRemoteFollowers(leaderID int) []NodeRPC { 23 | buf := make([]NodeRPC, len(c.allNodes)-1) 24 | j := 0 25 | for i, n := range c.allNodes { 26 | if i == leaderID { 27 | continue 28 | } 29 | buf[j] = n 30 | j++ 31 | } 32 | return buf 33 | } 34 | 35 | // StartAll stops all nodes in the cluster. 36 | func (c *Cluster) StartAll() { 37 | for _, n := range c.allNodes { 38 | n.Start(c) 39 | } 40 | } 41 | 42 | // StopAll stops all nodes in the cluster. 43 | func (c *Cluster) StopAll() { 44 | for _, n := range c.allNodes { 45 | n.Stop() 46 | } 47 | } 48 | 49 | // StopLeader stops the leader in the cluster. 50 | func (c *Cluster) StopLeader() *Node { 51 | for _, n := range c.allNodes { 52 | if n.isLeader() { 53 | n.Stop() 54 | return n 55 | } 56 | } 57 | return nil // no leader found 58 | } 59 | 60 | // Check checks if a cluster is in a valid state. 61 | // There should be exact one leader. 62 | func (c *Cluster) Check() (bool, error) { 63 | leaderCount := 0 64 | followerCount := 0 65 | candidateCount := 0 66 | for _, n := range c.allNodes { 67 | if n.isLeader() { 68 | leaderCount++ 69 | } else if n.isCandidate() { 70 | candidateCount++ 71 | } else if n.isFollower() { 72 | followerCount++ 73 | } 74 | } 75 | if leaderCount > 1 { 76 | return false, errors.New("there are multiple leaders in the cluster") 77 | } else if leaderCount == 0 { 78 | return false, errors.New("there is no leader in the cluster") 79 | } else if followerCount < len(c.allNodes)/2 { 80 | return false, errors.New("there are not enough followers -> split brain") 81 | } 82 | return true, nil 83 | } 84 | -------------------------------------------------------------------------------- /dp/kvstore/core/raft/cluster_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package raft is an implementation of the RAFT consensus algorithm. 5 | package raft 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func TestBigCluster(t *testing.T) { 13 | 14 | n1 := NewNode(0) 15 | n2 := NewNode(1) 16 | n3 := NewNode(2) 17 | n4 := NewNode(3) 18 | n5 := NewNode(4) 19 | 20 | nodes := []*Node{n1, n2, n3, n4, n5} 21 | 22 | cluster := NewCluster(nodes) 23 | 24 | cluster.StartAll() 25 | 26 | time.Sleep(10000 * time.Millisecond) 27 | 28 | ok, err := cluster.Check() 29 | if !ok { 30 | t.Error(err) 31 | } 32 | 33 | cluster.StopAll() 34 | time.Sleep(1000 * time.Millisecond) // wait for grace shutdown 35 | } 36 | 37 | func TestHeartbeat(t *testing.T) { 38 | // single node cluster 39 | n1 := NewNode(0) 40 | nodes := []*Node{n1} 41 | n1.cluster = NewCluster(nodes) 42 | 43 | // startHeartbeat is only allowed in leader state 44 | n1.statemachine.Next(CANDIDATE) 45 | n1.statemachine.Next(LEADER) 46 | n1.heartbeatTimer.resetC <- true 47 | 48 | // wait two second --> check console output 49 | time.Sleep(2000 * time.Millisecond) 50 | n1.Stop() 51 | } 52 | -------------------------------------------------------------------------------- /dp/kvstore/core/raft/configuration.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package raft is an implementation of the RAFT consensus algorithm. 5 | package raft 6 | 7 | // Configuration contains setup information for initializing a remote cluster. 8 | type Configuration struct { 9 | } 10 | -------------------------------------------------------------------------------- /dp/kvstore/core/raft/node-utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package raft is an implementation of the RAFT consensus algorithm. 5 | package raft 6 | 7 | import ( 8 | "log" 9 | "time" 10 | ) 11 | 12 | func (n *Node) log(msg string) { 13 | log.Printf("[%v] [%v] [%v] : %v", n.id, n.statemachine.Current(), n.currentTerm, msg) 14 | } 15 | 16 | func (n *Node) isLeader() bool { 17 | return n.statemachine.Current() == LEADER 18 | } 19 | 20 | func (n *Node) isFollower() bool { 21 | return n.statemachine.Current() == FOLLOWER 22 | } 23 | 24 | func (n *Node) isCandidate() bool { 25 | return n.statemachine.Current() == CANDIDATE 26 | } 27 | 28 | func (n *Node) isNotLeader() bool { 29 | return n.isFollower() || n.isCandidate() 30 | } 31 | 32 | type timercontrol struct { 33 | stopC chan bool 34 | resetC chan bool 35 | } 36 | 37 | func createPeriodicTimer(d func() time.Duration, timeout func()) timercontrol { 38 | stopC := make(chan bool) 39 | resetC := make(chan bool) 40 | go func() { 41 | timer := time.NewTimer(d()) 42 | timer.Stop() // Timer must be started explicit by using the reset channel 43 | for { 44 | select { 45 | case <-stopC: 46 | // log.Println("Timer Stopped") 47 | timer.Stop() 48 | break 49 | case <-timer.C: 50 | // log.Println("Timer Timeout") 51 | timer.Stop() 52 | timer.Reset(d()) 53 | go timeout() 54 | break 55 | case <-resetC: 56 | // log.Println("Timer Reset") 57 | timer.Stop() 58 | timer.Reset(d()) 59 | } 60 | } 61 | }() 62 | return timercontrol{stopC, resetC} 63 | } 64 | -------------------------------------------------------------------------------- /dp/kvstore/core/raft/node_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package raft is an implementation of the RAFT consensus algorithm. 5 | package raft 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func TestElection(t *testing.T) { 13 | 14 | n1 := NewNode(0) 15 | n2 := NewNode(1) 16 | n3 := NewNode(2) 17 | 18 | nodes := []*Node{n1, n2, n3} 19 | cluster := NewCluster(nodes) 20 | defer cluster.StopAll() 21 | 22 | cluster.StartAll() 23 | 24 | time.Sleep(4000 * time.Millisecond) 25 | 26 | ok, err := cluster.Check() 27 | if !ok { 28 | t.Error(err) 29 | } 30 | 31 | cluster.StopAll() 32 | } 33 | 34 | func TestFailover(t *testing.T) { 35 | 36 | n1 := NewNode(0) 37 | n2 := NewNode(1) 38 | n3 := NewNode(2) 39 | 40 | nodes := []*Node{n1, n2, n3} 41 | cluster := NewCluster(nodes) 42 | 43 | cluster.StartAll() 44 | defer cluster.StopAll() 45 | 46 | time.Sleep(5000 * time.Millisecond) 47 | 48 | cluster.StopLeader() 49 | 50 | time.Sleep(5000 * time.Millisecond) 51 | 52 | ok, err := cluster.Check() 53 | if !ok { 54 | t.Error(err) 55 | } 56 | } 57 | 58 | func TestFailoverResume(t *testing.T) { 59 | 60 | n1 := NewNode(0) 61 | n2 := NewNode(1) 62 | n3 := NewNode(2) 63 | 64 | nodes := []*Node{n1, n2, n3} 65 | 66 | cluster := NewCluster(nodes) 67 | 68 | cluster.StartAll() 69 | defer cluster.StopAll() 70 | 71 | time.Sleep(5000 * time.Millisecond) 72 | 73 | // stop leader 74 | ns := cluster.StopLeader() 75 | 76 | time.Sleep(6000 * time.Millisecond) 77 | 78 | // resume old leader -> get follower 79 | ns.Start(cluster) 80 | 81 | time.Sleep(2000 * time.Millisecond) 82 | 83 | ok, err := cluster.Check() 84 | if !ok { 85 | t.Error(err) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /dp/kvstore/core/raft/noderpc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package raft is an implementation of the RAFT consensus algorithm. 5 | package raft 6 | 7 | // NodeRPC is the remote interface for Node to Node communication in the RAFT cluster. 8 | type NodeRPC interface { 9 | 10 | // AppendEntries is used by the Leader to replicate logs and it is used as heartbeat. 11 | // The Leader will call the AppendEntries method on all nodes in the cluster. 12 | // Arguments 13 | // - term : leaders term 14 | // - leaderId : leadersId to redirect calls to leader 15 | // - prevLogIndex : Index of log entry immediately processing 16 | // - prevLogTerm : Term of the prevLogIndex entry 17 | // - entries : log entries to store (empty for heartbeat) 18 | // - leaderCommit : leaders commit index 19 | // Results 20 | // - term : current termin (for leader, update itself) 21 | // - success : true if follower contained entry matching prevLogIndex and prevLogTerm 22 | AppendEntries(term, leaderID, prevLogIndex, prevLogTerm int, entries []string, 23 | leaderCommit int) (int, bool) 24 | 25 | // RequestVote is called by candidates to gather votes. 26 | // It returns the current term to update the candidate 27 | // It returns true when the candidate received vote. 28 | // Arguments 29 | // - term : candidates term 30 | // - candidateID : candidate requesting vote 31 | // - lastLogIndex : index of candidates last log entry 32 | // - lastLogTerm : term of candidates last log entry 33 | // Results 34 | // - term : the current term, for candidate to update itself 35 | // - voteGranted : true means candidate received vote 36 | RequestVote(term, candidateID, lastLogIndex, lastLogTerm int) (int, bool) 37 | } 38 | -------------------------------------------------------------------------------- /dp/kvstore/core/raft/replicatedlog.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package raft is an implementation of the RAFT consensus algorithm. 5 | package raft 6 | 7 | // ReplicatedLog is the transactional log for RAFT. 8 | type ReplicatedLog struct { 9 | } 10 | 11 | // NewReplicatedLog constructs a ReplicatedLog. 12 | func NewReplicatedLog() *ReplicatedLog { 13 | return new(ReplicatedLog) 14 | } 15 | -------------------------------------------------------------------------------- /dp/kvstore/core/raft/statemachine.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package raft is an implementation of the RAFT consensus algorithm. 5 | package raft 6 | 7 | import "fmt" 8 | 9 | // State represents the internal raft state. See RAFT paper figure 4. 10 | type State int 11 | 12 | const ( 13 | // FOLLOWER is the start state. 14 | FOLLOWER State = iota 15 | // CANDIDATE is the candidate state. 16 | CANDIDATE 17 | // LEADER is the leader state. 18 | LEADER 19 | ) 20 | 21 | // Stringer interface. 22 | func (s State) String() string { 23 | switch s { 24 | case FOLLOWER: 25 | return "FOLLOWER" 26 | case LEADER: 27 | return "LEADER" 28 | case CANDIDATE: 29 | return "CANDIDATE" 30 | default: 31 | panic("Unknown State! Should never happen.") 32 | } 33 | } 34 | 35 | // Statemachine encapsulates the current state and ensures only valid state changes are executed. 36 | type Statemachine struct { 37 | current State 38 | validTransitions map[State][]State 39 | } 40 | 41 | // Valid transitions: 42 | // <-- 43 | // +-----------------------+ 44 | // v | 45 | // +---------------+ +------------------+ +-------------+ 46 | // | Follower + -> | Candidate + -> | Leader | 47 | // +---------------+ +------------------+ +-------------+ 48 | // ^ ^ | | 49 | // | +-----+ | 50 | // | <-- | 51 | // +--------------------------------------------+ 52 | // <-- 53 | 54 | // NewStatemachine returns a new Statemachine in the FOLLOWER State. 55 | func NewStatemachine() *Statemachine { 56 | s := new(Statemachine) 57 | s.current = FOLLOWER 58 | s.validTransitions = map[State][]State{ 59 | FOLLOWER: []State{FOLLOWER, CANDIDATE}, 60 | CANDIDATE: []State{FOLLOWER, CANDIDATE, LEADER}, 61 | LEADER: []State{FOLLOWER}, 62 | } 63 | return s 64 | } 65 | 66 | // Next advances the state and make sure only valid transitions are made. 67 | func (s *Statemachine) Next(next State) { 68 | if !s.IsTransitionValid(next) { 69 | panic(fmt.Sprintf("State change from %v to %v is not allowed.", s.current, next)) 70 | } 71 | s.current = next 72 | } 73 | 74 | // Current returns the current state. 75 | func (s *Statemachine) Current() State { 76 | return s.current 77 | } 78 | 79 | // IsTransitionValid returns true if next state is a possible transition. 80 | func (s *Statemachine) IsTransitionValid(next State) bool { 81 | nextStates := s.validTransitions[s.current] 82 | for _, v := range nextStates { 83 | if v == next { 84 | return true // found 85 | } 86 | } 87 | return false // not found 88 | } 89 | -------------------------------------------------------------------------------- /dp/kvstore/core/raft/timer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package raft is an implementation of the RAFT consensus algorithm. 5 | package raft 6 | 7 | import ( 8 | "log" 9 | "testing" 10 | "time" 11 | ) 12 | 13 | func TestTimer(t *testing.T) { 14 | 15 | tc := createPeriodicTimer(func() time.Duration { 16 | return time.Duration(1000) * time.Millisecond 17 | }, func() { log.Println("Timeout") }) 18 | 19 | tc.resetC <- true 20 | time.Sleep(2 * time.Second) 21 | tc.stopC <- true 22 | time.Sleep(1 * time.Second) 23 | tc.resetC <- true 24 | time.Sleep(500 * time.Millisecond) 25 | tc.resetC <- true 26 | time.Sleep(500 * time.Millisecond) 27 | tc.resetC <- true 28 | 29 | time.Sleep(5 * time.Second) 30 | } 31 | -------------------------------------------------------------------------------- /dp/kvstore/kvstore-api.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package kvstore contains the KeyValueStore API. 5 | package kvstore 6 | 7 | // KVStore is the business interface to a distributed key value store. 8 | type KVStore interface { 9 | 10 | // Sets a value for a given key. 11 | SetString(key, value string) 12 | 13 | // GetString returns the value for a given key. 14 | GetString(key string) string 15 | } 16 | -------------------------------------------------------------------------------- /dp/kvstore/remote/dkvs.go: -------------------------------------------------------------------------------- 1 | package remote 2 | -------------------------------------------------------------------------------- /dp/kvstore/remote/proxy.go: -------------------------------------------------------------------------------- 1 | package remote 2 | -------------------------------------------------------------------------------- /dp/kvstore/remote/stub.go: -------------------------------------------------------------------------------- 1 | package remote 2 | -------------------------------------------------------------------------------- /fp/clojures/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // intSeq returns another function, which we define anonymously in the body of intSeq. 6 | // The returned function closes over the variable i to form a closure. 7 | func intSeq() func() int { 8 | i := 0 9 | return func() int { 10 | i++ 11 | return i 12 | } 13 | } 14 | func main() { 15 | // We call intSeq, assigning the result (a function) to nextInt. 16 | // This function value captures its own i value, which will be updated each time we call nextInt. 17 | 18 | nextInt := intSeq() 19 | // See the effect of the closure by calling nextInt a few times. 20 | fmt.Println(nextInt()) 21 | fmt.Println(nextInt()) 22 | 23 | // To confirm that the state is unique to that particular function, create and test a new one. 24 | newInts := intSeq() 25 | fmt.Println(newInts()) 26 | } 27 | 28 | // EOF OMIT 29 | -------------------------------------------------------------------------------- /fp/composition/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | func main() { 10 | 11 | f := func(x int) int { return x * x } 12 | g := func(x int) int { return x + 1 } 13 | 14 | // Functional Composition: (g◦f)(x) 15 | gf := func(x int) int { return g(f(x)) } 16 | 17 | fmt.Printf("%v\n", gf(2)) // --> 5 18 | 19 | // Generic Composition 20 | type any interface{} 21 | type function func(any) any 22 | 23 | compose := func(g, f function) function { 24 | return func(x any) any { 25 | return g(f(x)) 26 | } 27 | } 28 | square := func(x any) any { return x.(int) * x.(int) } 29 | f2 := func(x any) any { return f(x.(int)) } 30 | 31 | fmt.Printf("%v\n", compose(square, f2)(2)) // --> 16 32 | fmt.Printf("%v\n", compose(compose(square, f2), f2)(2)) // --> 256 33 | } 34 | -------------------------------------------------------------------------------- /fp/lambdacalculus/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | func main() { 10 | 11 | // Lambda Calculus in Golang --> See Video Graham Hutton 12 | // https://www.youtube.com/watch?v=eis11j_iGMs 13 | // Lambda Calculus with JS 14 | // TRUE = x => y => x; 15 | // FALSE = x => y => y; 16 | // NOT = b => b(FALSE)(TRUE); 17 | 18 | // This is the key: A Recursive function definition for all functions!!! 19 | type fnf func(fnf) fnf 20 | 21 | // λx.x is a function which returns itself (the ID) 22 | ID := func(x fnf) fnf { return x } 23 | 24 | // Functional Numbers ONE 25 | ONCE := func(f fnf) fnf { 26 | return func(x fnf) fnf { 27 | return f(x) 28 | } 29 | } 30 | 31 | // Functional Numbers TWO 32 | TWICE := func(f fnf) fnf { 33 | return func(x fnf) fnf { 34 | return f(f(x)) 35 | } 36 | } 37 | 38 | // Function Numbers THREE 39 | THRICE := func(f fnf) fnf { 40 | return func(x fnf) fnf { 41 | return f(f(f(x))) 42 | } 43 | } 44 | 45 | // Functional Numbers SUCCESSOR(N) => λwyx.y(wyx) 46 | SUCCESSOR := func(w fnf) fnf { 47 | return func(y fnf) fnf { 48 | return func(x fnf) fnf { 49 | return y(w)(y)(x) 50 | } 51 | } 52 | } 53 | 54 | Printer := func(x fnf) fnf { fmt.Print("."); return x } 55 | QUAD := TWICE(TWICE) 56 | QUAD(Printer)(ID) 57 | fmt.Println() 58 | 59 | SUCCESSOR(TWICE)(Printer)(ID) 60 | fmt.Println("SUCCESSOR(TWICE) = 3") 61 | 62 | SUCCESSOR(THRICE)(Printer)(ID) 63 | fmt.Println("SUCCESSOR(THRICE) = 4") 64 | 65 | // Boolean TRUE as function: λx.λy.x 66 | TRUE := func(x fnf) fnf { 67 | return func(y fnf) fnf { 68 | return x 69 | } 70 | } 71 | 72 | // Boolean FALSE as function: λx.λy.y 73 | FALSE := func(x fnf) fnf { 74 | return func(y fnf) fnf { 75 | return y 76 | } 77 | } 78 | 79 | // NOT as function: λb.b False True 80 | NOT := func(b fnf) fnf { 81 | return b(FALSE)(TRUE) 82 | } 83 | 84 | // AND as function: λxy.xy False 85 | AND := func(x fnf, y fnf) fnf { 86 | return x(y)(FALSE) 87 | } 88 | 89 | fmt.Printf("AND(true, true) = %p\n", AND(TRUE, TRUE)) 90 | fmt.Printf("AND(true, false) = %p\n", AND(TRUE, FALSE)) 91 | fmt.Printf("AND(false, true) = %p\n", AND(FALSE, TRUE)) 92 | 93 | fmt.Printf("Id = %p\n", ID) 94 | fmt.Printf("True = %p\n", TRUE) 95 | fmt.Printf("False = %p\n", FALSE) 96 | 97 | // should print false 98 | fmt.Printf("True(False)(True) = %p\n", TRUE(FALSE)(TRUE)) 99 | fmt.Printf("NOT(True) = %p\n", NOT(TRUE)) 100 | 101 | // should print true 102 | fmt.Printf("False(False)(True) = %p\n", FALSE(FALSE)(TRUE)) 103 | fmt.Printf("NOT(False) = %p\n", NOT(FALSE)) 104 | 105 | // debugging functions 106 | f := func(x fnf) fnf { fmt.Printf("f()\n"); return x } 107 | g := func(y fnf) fnf { fmt.Printf("g()\n"); return y } 108 | 109 | // select AND call first function f(ID) 110 | FALSE(FALSE)(TRUE)(f)(g)(ID) 111 | NOT(FALSE)(f)(g)(ID) 112 | 113 | // select AND call second function g(ID) 114 | TRUE(FALSE)(TRUE)(f)(g)(ID) 115 | NOT(TRUE)(f)(g)(ID) 116 | 117 | ONCE(NOT)(TRUE) // -> false 118 | TWICE(NOT)(TRUE) // -> true 119 | THRICE(NOT)(TRUE) // -> false 120 | 121 | fmt.Printf("ONCE(NOT)(TRUE) = %p\n", ONCE(NOT)(TRUE)) 122 | fmt.Printf("TWICE(NOT)(TRUE) = %p\n", TWICE(NOT)(TRUE)) 123 | } 124 | -------------------------------------------------------------------------------- /fp/parser/boolparser.go: -------------------------------------------------------------------------------- 1 | /** 2 | (C) Copyright 2018 Armin Heller 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | */ 17 | 18 | /** 19 | Start from the bottom of this file and finish at the top. 20 | Implement the functions below such that all the tests succeed. 21 | In the end, use the main function to experiment with the finished parser. 22 | */ 23 | 24 | package parser 25 | 26 | import ( 27 | "container/list" 28 | 29 | "github.com/jweigend/concepts-of-programming-languages/oop/boolparser/ast" 30 | ) 31 | 32 | /** parseExpression parses the following grammar: 33 | Expression := Or Spaces* 34 | The syntax tree is exactly the one returned by Or. 35 | */ 36 | func parseExpression(Input Input) Result { 37 | return Parser(parseOr).AndThen(ExpectSpaces.Optional()).First()(Input) 38 | } 39 | 40 | /** parseOr parses the following grammar: 41 | Or := And ^ ("|" ^ Or)? 42 | If the parser for ("|" ^ Or)? produces nothing then parseOr will return the 43 | tree returned by And. Otherwise parseOr will return a new Or Node containing 44 | the sub-trees returned by the recursive calls. parseOr uses expect to parse 45 | the symbol "|", i. e. it actually allows for Space* ^ "|". 46 | */ 47 | func parseOr(Input Input) Result { 48 | return Parser(parseAnd).AndThen(expect("|").AndThen(parseOr).Second().Optional()).Convert(makeOr)(Input) 49 | } 50 | 51 | /** parseAnd parses the following grammar: 52 | And := Not ^ ("&" ^ And)? 53 | If the parser for ("&" ^ And)? produces nothing then parseAnd will return the 54 | tree returned by Not. Otherwise parseAnd will return a new And Node containing 55 | the sub-trees returned by the recursive calls. parseAnd uses expect to parse 56 | the symbol "&", i. e. it actually allows for Space* ^ "&". 57 | */ 58 | func parseAnd(Input Input) Result { 59 | return Parser(parseNot).AndThen(expect("&").AndThen(parseAnd).Second().Optional()).Convert(makeAnd)(Input) 60 | } 61 | 62 | /** parseNot parses the following grammar: 63 | Not := "!"* ^ Atom 64 | It delegates parsing "!"* to parseExclamationMarks and the construction of Not 65 | nodes to makeNots. If there's no exclamation mark then parseNot will return 66 | the tree parsed by parseAtom. Otherwise parseNot will wrap the atom in as many 67 | Not nodes as there are exclamation marks. 68 | */ 69 | func parseNot(Input Input) Result { 70 | return parseExclamationMarks.AndThen(parseAtom).Convert(func(arg interface{}) interface{} { 71 | var pair = arg.(Pair) 72 | return makeNot(pair.First.(int), pair.Second.(ast.Node)) 73 | })(Input) 74 | } 75 | 76 | /** parseExclamationMarks parses the following grammar: 77 | "!"* 78 | It returns the number of exclamation marks in Result.Result as an int. 79 | parseExclamationMarks uses expect to parse the symbol "!", i. e. it actually 80 | allows for Space* ^ "!". 81 | */ 82 | var parseExclamationMarks Parser = func(Input Input) Result { 83 | return expect("!").Repeated().Convert(func(arg interface{}) interface{} { 84 | var list = arg.(*list.List) 85 | return list.Len() 86 | })(Input) 87 | } 88 | 89 | /** parseAtom parses the followiong grammar: 90 | Atom := Variable 91 | | "(" ^ Expression ^ ")" 92 | The parenthesis won't appear in the abstract syntax tree. parseAtom uses 93 | Parser.First() and Parser.Second() to extract the tree returned by 94 | parseExpression. 95 | */ 96 | func parseAtom(Input Input) Result { 97 | return parseVariable.OrElse(expect("(").AndThen(parseExpression).AndThen(expect(")")).First().Second())(Input) 98 | } 99 | 100 | /** parseVariable parses the following grammar: 101 | Variable := [a-zA-Z_][a-zA-Z_0-9]* 102 | It delegates parsing the variable name to ExpectIdentifier from the parser 103 | combinators package and uses the Convert method on parsers to create the 104 | ast.Val node. 105 | */ 106 | var parseVariable Parser = func(Input Input) Result { 107 | return MaybeSpacesBefore(ExpectIdentifier).Convert(func(arg interface{}) interface{} { 108 | var name = arg.(string) 109 | return ast.Val{Name: name} 110 | })(Input) 111 | } 112 | 113 | /** makeNot wraps the node into num ast.Not nodes. 114 | */ 115 | func makeNot(num int, node ast.Node) ast.Node { 116 | if num <= 0 { 117 | return node 118 | } 119 | return ast.Not{Ex: makeNot(num-1, node)} 120 | } 121 | 122 | /** makeAnd takes a Pair of ast.Node as an argument and returns an 123 | ast.Node. If the second component of the pair is equal to Nothing{} then it 124 | returns the first component of the Pair. If the second component is a Node 125 | then makeAnd will create an ast.And node containing the first and the second 126 | component of the Pair as sub-nodes. 127 | */ 128 | func makeAnd(argument interface{}) interface{} { 129 | var pair = argument.(Pair) 130 | if pair.Second == (Nothing{}) { 131 | return pair.First 132 | } 133 | var firstNode = pair.First.(ast.Node) 134 | var secondNode = pair.Second.(ast.Node) 135 | return ast.And{LHS: firstNode, RHS: secondNode} 136 | 137 | } 138 | 139 | /** makeOr takes a Pair of ast.Node as an argument and returns an 140 | ast.Node. If the second component of the pair is equal to Nothing{} then it 141 | returns the first component of the Pair. If the second component is a Node 142 | then makeOr will create an ast.Or node containing the first and the second 143 | component of the Pair as sub-nodes. 144 | */ 145 | func makeOr(argument interface{}) interface{} { 146 | var pair = argument.(Pair) 147 | if pair.Second == (Nothing{}) { 148 | return pair.First 149 | } 150 | var firstNode = pair.First.(ast.Node) 151 | var secondNode = pair.Second.(ast.Node) 152 | return ast.Or{LHS: firstNode, RHS: secondNode} 153 | } 154 | 155 | /** expect expects the string s at the beginning of the Input and ignores 156 | leading spaces. */ 157 | func expect(s string) Parser { 158 | return MaybeSpacesBefore(ExpectString(s)) 159 | } 160 | -------------------------------------------------------------------------------- /fp/parser/boolparser_test.go: -------------------------------------------------------------------------------- 1 | /** 2 | (C) Copyright 2018 Armin Heller 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | */ 17 | 18 | package parser 19 | 20 | import ( 21 | "testing" 22 | 23 | . "github.com/jweigend/concepts-of-programming-languages/oop/boolparser/ast" 24 | ) 25 | 26 | func TestMakeOr(t *testing.T) { 27 | var result = makeOr(Pair{Val{"a"}, Val{"b"}}) 28 | var expected Node = Or{Val{"a"}, Val{"b"}} 29 | if result != expected { 30 | t.Errorf( 31 | "makeOr (Pair { Val { \"a\" }, Val { \"b\" }}) failed! Expected %v"+ 32 | " but got wrong result %v !", expected, result) 33 | } 34 | result = makeOr(Pair{Val{"a"}, Nothing{}}) 35 | expected = Val{"a"} 36 | if result != expected { 37 | t.Errorf( 38 | "makeOr (Pair { Val { \"a\" }, Nothing{} }) failed! Expected %v "+ 39 | " but got wrong result %v !", expected, result) 40 | } 41 | } 42 | 43 | func TestMakeAnd(t *testing.T) { 44 | var result = makeAnd(Pair{Val{"a"}, Val{"b"}}) 45 | var expected Node = And{Val{"a"}, Val{"b"}} 46 | if result != expected { 47 | t.Errorf( 48 | "makeAnd (Pair { Val { \"a\" }, Val { \"b\" }}) failed! Expected %v"+ 49 | " but got wrong result %v !", expected, result) 50 | } 51 | result = makeAnd(Pair{Val{"a"}, Nothing{}}) 52 | expected = Val{"a"} 53 | if result != expected { 54 | t.Errorf( 55 | "makeAnd (Pair { Val { \"a\" }, Nothing{} }) failed! Expected %v "+ 56 | " but got wrong result %v !", expected, result) 57 | } 58 | } 59 | 60 | func TestMakeNot(t *testing.T) { 61 | var result = makeNot(0, Val{"a"}) 62 | var expected Node = Val{"a"} 63 | if result != expected { 64 | t.Errorf("makeNot (0, Val { \"a\" }) failed! Expected %v "+ 65 | "but got wrong result %v !", expected, result) 66 | } 67 | expected = Not{Not{Not{Val{"a"}}}} 68 | result = makeNot(3, Val{"a"}) 69 | if result != expected { 70 | t.Errorf("makeNot (3, Val { \"a\" }) failed! Expected %v "+ 71 | "but got wrong result %v !", expected, result) 72 | } 73 | } 74 | 75 | func TestParseVariable(t *testing.T) { 76 | var text = "xyz" 77 | var expected Node = Val{"xyz"} 78 | var result = parseVariable(StringToInput(text)) 79 | if result.Result != expected { 80 | t.Errorf("parseVariable on input \"%v\" failed! Expected %v "+ 81 | "but got wrong result %v !", text, expected, result.Result) 82 | } 83 | if result.RemainingInput != nil { 84 | var inp = result.RemainingInput.(RuneArrayInput) 85 | var rest = inp.Text[inp.CurrentPosition:] 86 | t.Errorf("parseVariable didn't eat all the input. Leftover: \"%v\"", 87 | string(rest)) 88 | } 89 | } 90 | 91 | func TestParseExclamationMarks(t *testing.T) { 92 | var text = "!!!x" 93 | var expected int = 3 94 | var result = parseExclamationMarks(StringToInput(text)) 95 | if result.Result != expected { 96 | t.Errorf("parseExclamationMarks on input \"%v\" failed! Expected %d "+ 97 | "but got wrong result %d !", text, expected, result.Result) 98 | } 99 | if result.RemainingInput != nil { 100 | var inp = result.RemainingInput.(RuneArrayInput) 101 | var rest = inp.Text[inp.CurrentPosition:] 102 | if "x" != string(rest) { 103 | t.Errorf("parseExclamationMarks ate the wrong amout of input! "+ 104 | "Leftover: \"%v\"", string(rest)) 105 | } 106 | } else { 107 | t.Errorf("parseExclamationMarks mustn't eat all the input of \"%v\" "+ 108 | "but it did!", text) 109 | } 110 | 111 | } 112 | 113 | func testExp(t *testing.T, text string, expected Node) { 114 | var result = parseExpression(StringToInput(text)) 115 | if result.Result != expected { 116 | t.Errorf("parseExpression on input \"%v\" failed! Expected %v "+ 117 | "but got wrong result %v !", text, expected, result.Result) 118 | } 119 | if result.RemainingInput != nil { 120 | var inp = result.RemainingInput.(RuneArrayInput) 121 | var rest = inp.Text[inp.CurrentPosition:] 122 | t.Errorf("parseExpression didn't eat all the input. "+ 123 | "Leftover: \"%v\"", string(rest)) 124 | } 125 | } 126 | 127 | func TestParseExpression(t *testing.T) { 128 | testExp(t, "!a", Not{Val{"a"}}) 129 | testExp(t, "a&b", And{Val{"a"}, Val{"b"}}) 130 | testExp(t, "a|b", Or{Val{"a"}, Val{"b"}}) 131 | testExp(t, " a & b", And{Val{"a"}, Val{"b"}}) 132 | testExp(t, "a ", Val{"a"}) 133 | testExp(t, "a&b&c", And{Val{"a"}, And{Val{"b"}, Val{"c"}}}) 134 | testExp(t, "a&(b&c)", And{Val{"a"}, And{Val{"b"}, Val{"c"}}}) 135 | testExp(t, "(a&b)&c", And{And{Val{"a"}, Val{"b"}}, Val{"c"}}) 136 | testExp(t, "a|b|c", Or{Val{"a"}, Or{Val{"b"}, Val{"c"}}}) 137 | testExp(t, "a|(b|c)", Or{Val{"a"}, Or{Val{"b"}, Val{"c"}}}) 138 | testExp(t, "(a|b)|c", Or{Or{Val{"a"}, Val{"b"}}, Val{"c"}}) 139 | testExp(t, "!a & b|c&!(d|e)", 140 | Or{And{Not{Val{"a"}}, Val{"b"}}, 141 | And{Val{"c"}, Not{Or{Val{"d"}, Val{"e"}}}}}) 142 | 143 | } 144 | -------------------------------------------------------------------------------- /fp/streams/streams.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package streams contains a minimal Java8 Streams like API for slices. 5 | // Usage: 6 | // result := ToStream(slice). 7 | // Map(toUpperCase). 8 | // Filter(containsDigit). 9 | // Reduce(concat).(string) 10 | // - ToStream() converts a Slice of Any (interface{}) to a Steam. 11 | // - Map() applies a function to all elements. 12 | // - Filter() filters all elements out which not match the given predicate. 13 | // - Reduce() combines all elements and returns a single element. 14 | // This is a very first draft with no lazy or parallel support. 15 | package streams 16 | 17 | // Any is a shortcut for the empty interface{}. 18 | type Any interface{} 19 | 20 | // Predicate function returns true if a given element should be filtered. 21 | type Predicate func(Any) bool 22 | 23 | // Mapper function maps a value to another value. 24 | type Mapper func(o1 Any) Any 25 | 26 | // Accumulator function returns a combined element. 27 | type Accumulator func(Any, Any) Any 28 | 29 | // Pair of two values. 30 | type Pair struct { 31 | k Any 32 | v Any 33 | } 34 | 35 | type Iterator interface { 36 | HasNext() bool 37 | Next() Any 38 | } 39 | 40 | // Stream interface is implemented for container types. 41 | type Stream interface { 42 | Iterator() Iterator 43 | Map(m Mapper) Stream 44 | Filter(p Predicate) Stream 45 | Reduce(a Accumulator) Any 46 | } 47 | 48 | type SliceIterator struct { 49 | slice []Any 50 | currentPos int 51 | } 52 | 53 | func NewSliceIterator(slice []Any) *SliceIterator { 54 | result := new(SliceIterator) 55 | result.slice = slice 56 | return result 57 | } 58 | 59 | func (s *SliceIterator) HasNext() bool { 60 | return len(s.slice) > s.currentPos 61 | } 62 | 63 | func (s *SliceIterator) Next() Any { 64 | if len(s.slice) > s.currentPos { 65 | cp := s.currentPos 66 | s.currentPos++ 67 | return s.slice[cp] 68 | } 69 | panic("No such element") 70 | } 71 | 72 | // ToStream helper converts a slice into a Stream. 73 | func ToStream(s []Any) Stream { 74 | return NewSliceStream(s) 75 | } 76 | 77 | // SliceStream is a stream implementation for slices. 78 | type SliceStream struct { 79 | data []Any 80 | } 81 | 82 | // NewSliceStream returns a new stream. 83 | func NewSliceStream(data []Any) *SliceStream { 84 | ss := new(SliceStream) 85 | ss.data = data 86 | return ss 87 | } 88 | 89 | // Map applies the Mapper on all elements. 90 | func (s *SliceStream) Map(mapper Mapper) Stream { 91 | for i, e := range s.data { 92 | s.data[i] = mapper(e) 93 | } 94 | return s 95 | } 96 | 97 | // Filter filters all elements out. 98 | func (s *SliceStream) Filter(filter Predicate) Stream { 99 | data := new([]Any) 100 | for _, e := range s.data { 101 | if filter(e) { 102 | *data = append(*data, e) 103 | } 104 | } 105 | s.data = *data 106 | return s 107 | } 108 | 109 | // Reduce combines two elements and return one element. 110 | func (s *SliceStream) Reduce(accumulate Accumulator) Any { 111 | 112 | var result interface{} 113 | for i, e := range s.data { 114 | if i == 0 { 115 | result = e 116 | } else { 117 | result = accumulate(result, s.data[i]) 118 | } 119 | } 120 | return result 121 | } 122 | 123 | func (s *SliceStream) Iterator() Iterator { 124 | return nil 125 | } 126 | -------------------------------------------------------------------------------- /fp/streams/streams_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | package streams 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | "testing" 9 | ) 10 | 11 | // TestMapFilterReduce tests the Java 8 Map/Filter/Reduce Chain. 12 | func TestMapFilterReduce(t *testing.T) { 13 | // array of generic interfaces. 14 | stringSlice := []Any{"a", "b", "c", "1", "D"} 15 | 16 | // Map/Reduce 17 | result := ToStream(stringSlice). 18 | Map(toUpperCase). 19 | Filter(notDigit). 20 | Reduce(concat).(string) 21 | 22 | if result != "A,B,C,D" { 23 | t.Error(fmt.Sprintf("Result should be 'A,B,C,D' but is: %v", result)) 24 | } 25 | // lambda (inline) 26 | result = ToStream(stringSlice). 27 | Map(func(o Any) Any { 28 | return strings.ToUpper(o.(string)) 29 | }). 30 | Filter(func(o Any) bool { 31 | s := o.(string) 32 | result := true 33 | for _, v := range s { 34 | if v >= '0' && v <= '9' { 35 | result = false 36 | break 37 | } 38 | } 39 | return result 40 | }). 41 | Reduce(func(a Any, b Any) Any { 42 | return a.(string) + "," + b.(string) 43 | }).(string) 44 | } 45 | 46 | // toUpperCase converts a given string to upper case. 47 | func toUpperCase(o Any) Any { 48 | return strings.ToUpper(o.(string)) 49 | } 50 | 51 | //EOF OMIT 52 | 53 | // notDigit loops over a string value and checks if the string contains a digit. 54 | func notDigit(o Any) bool { 55 | s := o.(string) 56 | result := true 57 | for _, v := range s { 58 | if v >= '0' && v <= '9' { 59 | result = false 60 | break 61 | } 62 | } 63 | return result 64 | } 65 | 66 | // concat produces a string by concating two strings with ,. 67 | func concat(a Any, b Any) Any { 68 | return a.(string) + "," + b.(string) 69 | } 70 | 71 | // WC OMIT 72 | // 73 | // ======================== 74 | // Classic wordcount sample 75 | // ======================== 76 | // 77 | func TestWordCount(t *testing.T) { 78 | strings := []Any{"a", "a", "b", "b", "D", "a"} 79 | 80 | // Map/Reduce 81 | result := ToStream(strings). 82 | Map(func(o Any) Any { 83 | result := []Pair{Pair{o, 1}} 84 | return result 85 | }). 86 | Reduce(sumInts).([]Pair) 87 | 88 | for _, e := range result { 89 | fmt.Printf("%v:%v, ", e.k, e.v) 90 | } 91 | } 92 | 93 | // ENDWC OMIT 94 | 95 | // sumValues reduces the pair arrays by adding the count for the same key. 96 | func sumInts(a Any, b Any) Any { 97 | pa := a.([]Pair) 98 | pb := b.([]Pair) 99 | for i, e := range pa { 100 | for _, u := range pb { 101 | if e.k == u.k { 102 | pa[i].v = e.v.(int) + u.v.(int) 103 | return pa 104 | } 105 | } 106 | } 107 | result := append(pa, pb...) 108 | return result 109 | } 110 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/jweigend/concepts-of-programming-languages 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/QAhell/Parser-Gombinators v0.0.0-20181105095349-a77567bd48b8 7 | github.com/coreos/etcd v3.3.18+incompatible 8 | github.com/gogo/protobuf v1.3.1 // indirect 9 | github.com/golang/protobuf v1.3.2 10 | go.etcd.io/etcd v3.3.18+incompatible 11 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a 12 | google.golang.org/grpc v1.25.1 13 | ) 14 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/QAhell/Parser-Gombinators v0.0.0-20181105095349-a77567bd48b8 h1:VZE0EWpkbo7zcdvKS+nI8dSoERYyJsCavfFEDqigBN8= 4 | github.com/QAhell/Parser-Gombinators v0.0.0-20181105095349-a77567bd48b8/go.mod h1:cfw1yHS4cqLUrhJpjwdH0kVIDJIlcpnmj9zvky3D8PM= 5 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 6 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 7 | github.com/coreos/etcd v3.3.18+incompatible h1:Zz1aXgDrFFi1nadh58tA9ktt06cmPTwNNP3dXwIq1lE= 8 | github.com/coreos/etcd v3.3.18+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 9 | github.com/coreos/raft v0.0.0-20140324040310-67dca7288f16 h1:a67E9Q1T70maltCugEwRJWzopRnCr+FNL4ysVe2fkNM= 10 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 11 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 12 | github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= 13 | github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 14 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 15 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 16 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 17 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 18 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 19 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 20 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 21 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 22 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 23 | go.etcd.io/etcd v3.3.18+incompatible h1:5aomL5mqoKHxw6NG+oYgsowk8tU8aOalo2IdZxdWHkw= 24 | go.etcd.io/etcd v3.3.18+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= 25 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 26 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 27 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 28 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 29 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 30 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 31 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 32 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 33 | golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= 34 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 35 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 36 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 37 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 38 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 39 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 40 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= 41 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 42 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 43 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 44 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 45 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 46 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 47 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 48 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 49 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 50 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 51 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 52 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= 53 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 54 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 55 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 56 | google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0= 57 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 58 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 59 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 60 | -------------------------------------------------------------------------------- /oop/boolparser/ast/ast.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package ast contains an abstract syntax tree for boolean expressions. 5 | package ast 6 | 7 | import "fmt" 8 | 9 | // 10 | // ---------- AST ------------ 11 | // 12 | 13 | // Node is the interface to eval an abstract syntax tree (AST) 14 | type Node interface { 15 | 16 | // Eval evaluates the AST. The variables of the expression are set to true or false in the vars map. 17 | // Missing vars (there are no key in the map) are evaluated to false. 18 | Eval(vars map[string]bool) bool 19 | } 20 | 21 | // Or is the logical OR Operator in an AST 22 | type Or struct { 23 | LHS Node 24 | RHS Node 25 | } 26 | 27 | // Eval implements the Node interface 28 | func (o Or) Eval(vars map[string]bool) bool { 29 | return o.LHS.Eval(vars) || o.RHS.Eval(vars) 30 | } 31 | 32 | func (o Or) String() string { 33 | return fmt.Sprintf("|(%v,%v)", o.LHS, o.RHS) 34 | } 35 | 36 | // And is the logical AND Operator in an AST 37 | type And struct { 38 | LHS Node 39 | RHS Node 40 | } 41 | 42 | // Eval implements the Node interface 43 | func (a And) Eval(vars map[string]bool) bool { 44 | return a.LHS.Eval(vars) && a.RHS.Eval(vars) 45 | } 46 | 47 | func (a And) String() string { 48 | return fmt.Sprintf("&(%v,%v)", a.LHS, a.RHS) 49 | } 50 | 51 | // Not is the NOT operator in the AST 52 | type Not struct { 53 | Ex Node 54 | } 55 | 56 | // Eval implements the Node interface 57 | func (n Not) Eval(vars map[string]bool) bool { 58 | return !n.Ex.Eval(vars) 59 | } 60 | 61 | func (n Not) String() string { 62 | return fmt.Sprintf("!(%v)", n.Ex) 63 | } 64 | 65 | // Val is a boolean variable in an AST 66 | type Val struct { 67 | Name string 68 | } 69 | 70 | // Eval implements the Node interface 71 | func (v Val) Eval(vars map[string]bool) bool { 72 | return vars[v.Name] // missing vars will be evaluated to false! 73 | } 74 | 75 | func (v Val) String() string { 76 | return fmt.Sprintf("'%v'", v.Name) 77 | } 78 | -------------------------------------------------------------------------------- /oop/boolparser/ast/ast_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend, Johannes Siedersleben 2 | // Licensed under the Apache License, Version 2.0 3 | package ast 4 | 5 | import ( 6 | "testing" 7 | ) 8 | 9 | func TestAST(t *testing.T) { 10 | 11 | // AST for expression: "A AND B OR !C" 12 | ast := Or{And{Val{"A"}, Val{"B"}}, Not{Val{"C"}}} 13 | 14 | // Table to test all combinations for A, B, C -> 2^3 = 8 combinations. 15 | // Format of Table { Value for A, Value for B, Value for C, Expected Result } 16 | truthTable := [][]bool{ 17 | {false, false, false, true}, 18 | {false, false, true, false}, 19 | {false, true, true, false}, 20 | {false, true, false, true}, 21 | {true, false, false, true}, 22 | {true, true, false, true}, 23 | {true, false, true, false}, 24 | {true, true, true, true}, 25 | } 26 | 27 | // Test all possible combinations 28 | for _, tt := range truthTable { 29 | 30 | vars := map[string]bool{"A": tt[0], "B": tt[1], "C": tt[2]} 31 | expected := tt[3] 32 | result := ast.Eval(vars) 33 | 34 | if result != expected { 35 | t.Errorf("Expected %v but got %v. (Expression := %v, Vars := %v)", expected, result, ast, vars) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /oop/boolparser/lexer/lexer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package lexer contains an interpreter (parser/compiler) for boolean expressions with variables. 5 | package lexer 6 | 7 | // Lexer is a simple tokenizer for generating tokens for the boolean parser. 8 | type Lexer struct { 9 | input string 10 | tokens []string 11 | current int 12 | } 13 | 14 | // NewLexer constructs a lexer from a given string. 15 | func NewLexer(input string) *Lexer { 16 | lexer := new(Lexer) 17 | lexer.input = input 18 | lexer.current = 0 19 | lexer.tokens = splitTokens(input) 20 | return lexer 21 | } 22 | 23 | // NextToken returns the next token. A token is a non empty string. The function returns "" if there is no token available. 24 | func (l *Lexer) NextToken() string { 25 | if l.current == len(l.tokens) { 26 | return "" 27 | } 28 | token := l.tokens[l.current] 29 | l.current++ 30 | return token 31 | } 32 | 33 | func splitTokens(input string) []string { 34 | result := make([]string, 0) 35 | token := "" 36 | for i := 0; i < len(input); i++ { 37 | currentChar := input[i] 38 | if currentChar == byte(' ') { 39 | continue // ignore whitespace 40 | } 41 | // START OMIT 42 | switch currentChar { 43 | // check for terminal 44 | case byte('&'), byte('|'), byte('!'), byte('('), byte(')'): 45 | if token != "" { 46 | result = append(result, token) 47 | token = "" 48 | } 49 | result = append(result, string(currentChar)) 50 | break 51 | // var assumed 52 | default: 53 | token += string(currentChar) // concat var chars 54 | } 55 | // END OMIT 56 | } 57 | 58 | // append last token if exists 59 | if token != "" { 60 | result = append(result, token) 61 | } 62 | return result 63 | } 64 | -------------------------------------------------------------------------------- /oop/boolparser/lexer/lexer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | package lexer 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | ) 10 | 11 | func TestNewLexer(t *testing.T) { 12 | 13 | lexer := NewLexer("a & b") 14 | if len(lexer.tokens) != 3 { 15 | t.Error(fmt.Sprintf("Wrong token count! Expected: 3, Got: %v", len(lexer.tokens))) 16 | } 17 | 18 | lexer = NewLexer("a|b") 19 | if len(lexer.tokens) != 3 { 20 | t.Error(fmt.Sprintf("Wrong token count! Expected: 3, Got: %v", len(lexer.tokens))) 21 | } 22 | 23 | lexer = NewLexer("a|b&(b|c)") 24 | if len(lexer.tokens) != 9 { 25 | t.Error(fmt.Sprintf("Wrong token count! Expected: 9, Got: %v", len(lexer.tokens))) 26 | } 27 | 28 | tok := lexer.NextToken() 29 | for tok != "" { 30 | fmt.Print(tok) 31 | tok = lexer.NextToken() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /oop/boolparser/parser/parser.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package parser contains an parser/interpreter for boolean expressions. 5 | package parser 6 | 7 | import ( 8 | "fmt" 9 | "regexp" 10 | 11 | "github.com/jweigend/concepts-of-programming-languages/oop/boolparser/ast" 12 | "github.com/jweigend/concepts-of-programming-languages/oop/boolparser/lexer" 13 | ) 14 | 15 | // --------------------------------------------------------- 16 | // The expression should have the following EBNF form: 17 | // EBNF 18 | // --------------------------------------------------------- 19 | // ::= { } 20 | // ::= { } 21 | // ::= | | () 22 | // ::= '|' 23 | // ::= '&' 24 | // ::= '!' 25 | // ::= '[a-zA-Z0-9]*' 26 | // --------------------------------------------------------- 27 | 28 | // Parser is a recursive decent parser for boolean expressions. 29 | type Parser struct { 30 | rootNode ast.Node 31 | token string // ll(1) 32 | lexer *lexer.Lexer 33 | } 34 | 35 | // NewParser constructs a recursive descent parser and compiles the input of the lexer. 36 | func NewParser(lexer *lexer.Lexer) *Parser { 37 | b := Parser{lexer: lexer} 38 | b.parse() 39 | return &b 40 | } 41 | 42 | // Eval evaluates the AST tree against the given var map. 43 | func (p *Parser) Eval(vars map[string]bool) bool { 44 | return p.rootNode.Eval(vars) 45 | } 46 | 47 | // String implements interface Stringer. 48 | func (p *Parser) String() string { 49 | return fmt.Sprintf("%v", p.rootNode) 50 | } 51 | 52 | // 53 | // ---------- PARSING ------------ 54 | // 55 | 56 | // parse expr and build the AST. 57 | func (p *Parser) parse() { 58 | p.expression() 59 | } 60 | 61 | // see BNF expression. 62 | func (p *Parser) expression() { 63 | p.term() 64 | for p.token == "|" { 65 | lhs := p.rootNode 66 | p.term() 67 | rhs := p.rootNode 68 | p.rootNode = &ast.Or{LHS: lhs, RHS: rhs} 69 | } 70 | } 71 | 72 | // see BNF term. 73 | func (p *Parser) term() { 74 | p.factor() 75 | for p.token == "&" { 76 | lhs := p.rootNode 77 | p.factor() 78 | rhs := p.rootNode 79 | p.rootNode = &ast.And{LHS: lhs, RHS: rhs} 80 | } 81 | } 82 | 83 | // see BNF factor. 84 | func (p *Parser) factor() { 85 | p.token = p.lexer.NextToken() 86 | if p.token == "" { 87 | return // end 88 | } else if p.token == "!" { 89 | p.factor() 90 | p.rootNode = ast.Not{Ex: p.rootNode} 91 | } else if p.token == "(" { 92 | p.expression() 93 | p.token = p.lexer.NextToken() 94 | } else if isVar(p.token) { 95 | p.rootNode = ast.Val{Name: p.token} 96 | p.token = p.lexer.NextToken() 97 | } else { 98 | panic(fmt.Sprintf("Unknown symbol %v", p.token)) 99 | } 100 | } 101 | 102 | // isVar checks if a token is a identifier which starts with an ASCII Letter. 103 | func isVar(token string) bool { 104 | if len(token) == 0 { 105 | panic("Empty token!") 106 | } 107 | return validVar.MatchString(token) 108 | } 109 | 110 | // Regex for vars. 111 | var validVar = regexp.MustCompile("[a-zA-Z0-9]*") 112 | -------------------------------------------------------------------------------- /oop/boolparser/parser/parser_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | package parser 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | 10 | "github.com/jweigend/concepts-of-programming-languages/oop/boolparser/lexer" 11 | ) 12 | 13 | func TestParser_Eval(t *testing.T) { 14 | // create parser 15 | p := NewParser(lexer.NewLexer("a&b&!c")) 16 | 17 | // set vars 18 | vars := map[string]bool{ 19 | "a": true, 20 | "b": true, 21 | "c": false, 22 | } 23 | if p.Eval(vars) != true { 24 | t.Error(fmt.Sprintf("Wrong result detected")) 25 | } 26 | 27 | // set vars 28 | vars = map[string]bool{ 29 | "a": true, 30 | "b": true, 31 | "c": true, 32 | } 33 | if p.Eval(vars) != false { 34 | t.Error(fmt.Sprintf("Wrong result detected")) 35 | } 36 | 37 | // set vars 38 | vars = map[string]bool{ 39 | "a": false, 40 | "b": false, 41 | "c": false, 42 | } 43 | if p.Eval(vars) != false { 44 | t.Error(fmt.Sprintf("Wrong result detected")) 45 | } 46 | 47 | p = NewParser(lexer.NewLexer("a & (b | c & b) & d")) 48 | 49 | // set vars 50 | vars = map[string]bool{ 51 | "a": true, 52 | "b": true, 53 | "c": false, 54 | "d": true, 55 | } 56 | if p.Eval(vars) != true { 57 | t.Error(fmt.Sprintf("Wrong result detected")) 58 | } 59 | 60 | // test string support 61 | if p.String() != "&(&('a',|('b',&('c','b'))),'d')" { 62 | t.Error(fmt.Sprintf("Wrong string representation: %v", p.String())) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /oop/mail/client/client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend, Johannes Siedersleben 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package client contains sample code for the mail components. 5 | package client 6 | 7 | import ( 8 | "github.com/jweigend/concepts-of-programming-languages/oop/mail" 9 | "github.com/jweigend/concepts-of-programming-languages/oop/mail/util" 10 | ) 11 | 12 | // Registry is the central configration for the service locator 13 | var Registry = util.NewRegistry() 14 | 15 | // SendMail sends a mail to a receiver. 16 | func SendMail(address, message string) { 17 | 18 | // Create an implementation for the mail.Sender interface. 19 | var sender = Registry.Get("mail.Sender").(mail.Sender) 20 | 21 | mailaddrs := mail.Address{Address: address} 22 | sender.SendMail(mailaddrs, message) 23 | } 24 | 25 | // EOF OMIT 26 | -------------------------------------------------------------------------------- /oop/mail/client/client_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend, Johannes Siedersleben 2 | // Licensed under the Apache License, Version 2.0 3 | package client 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/jweigend/concepts-of-programming-languages/oop/mail/smtp" 9 | ) 10 | 11 | // configure Registry to know which mail implementation is used. 12 | func init() { 13 | Registry.Register("mail.Sender", new(smtp.MailSenderImpl)) 14 | } 15 | 16 | func TestMail(t *testing.T) { 17 | SendMail("johannes.weigend@qaware.de", "Hello from Go!") 18 | } 19 | -------------------------------------------------------------------------------- /oop/mail/mail.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend, Johannes Siedersleben 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package mail contains the Mail API interfaces and datatypes for sending Emails. 5 | package mail 6 | 7 | // Address is the address of the mail receiver. 8 | type Address struct { 9 | Address string 10 | } 11 | 12 | // Sender is a interface to send mails. 13 | type Sender interface { 14 | 15 | // Send an email to a given address with a message. 16 | SendMail(address Address, message string) 17 | } 18 | 19 | // END OMIT 20 | -------------------------------------------------------------------------------- /oop/mail/smtp/sender.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend, Johannes Siedersleben 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package smtp sends mails over the smtp protocol. 5 | package smtp 6 | 7 | import ( 8 | "log" 9 | 10 | "github.com/jweigend/concepts-of-programming-languages/oop/mail" 11 | ) 12 | 13 | // MailSenderImpl is a sender object. 14 | type MailSenderImpl struct { 15 | } 16 | 17 | // SendMail sends a mail to a receiver. 18 | func (m *MailSenderImpl) SendMail(address mail.Address, message string) { 19 | log.Println("Sending message with SMTP to " + address.Address + " message: " + message) 20 | return 21 | } 22 | 23 | //END OMIT 24 | -------------------------------------------------------------------------------- /oop/mail/util/registry.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend, Johannes Siedersleben 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package util contains a simple Service Locator. 5 | package util 6 | 7 | // Registry is a simple Service Locator for interfaces. 8 | type Registry struct { 9 | services map[string]interface{} 10 | } 11 | 12 | // NewRegistry constructor 13 | func NewRegistry() *Registry { 14 | registry := new(Registry) 15 | registry.services = make(map[string]interface{}) 16 | return registry 17 | } 18 | 19 | // Register registers a single interface for a unique name 20 | func (s *Registry) Register(name string, some interface{}) { 21 | s.services[name] = some 22 | } 23 | 24 | // Get returns the registered interface for a given name 25 | func (s *Registry) Get(name string) interface{} { 26 | result := s.services[name] 27 | return result 28 | } 29 | -------------------------------------------------------------------------------- /oop/polymorphism/geoobject/geoobject.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Position is the position of the screen. 8 | type Position struct { 9 | x, y int 10 | } 11 | 12 | // GeoObject is the "BaseClass" of any geometrical object. All objects have a position and a color. 13 | type GeoObject struct { 14 | pos Position 15 | color int 16 | } 17 | 18 | // Circle is a concrete GeoObject with a radius. 19 | type Circle struct { 20 | GeoObject 21 | radius int 22 | } 23 | 24 | // Rectangle is a concrete GeoObject with a width and a height. 25 | type Rectangle struct { 26 | GeoObject 27 | width, height int 28 | } 29 | 30 | // Triangle is a concrete GeoObject with three points (ABC). 31 | // The coordinates of the three points are relative to the position of the object. 32 | type Triangle struct { 33 | GeoObject 34 | p1, p2, p3 Position 35 | } 36 | 37 | // Painter is used to paint GeoObjects. 38 | type Painter interface { 39 | Paint() 40 | } 41 | 42 | // Paint is implemented by Circle 43 | func (c Circle) Paint() { 44 | fmt.Printf("Painting circle with radius=%v at position=%v and color=%v\n", c.radius, c.pos, c.color) 45 | } 46 | 47 | // Paint is implemented by Rectangle 48 | func (r Rectangle) Paint() { 49 | fmt.Printf("Painting rectangle with width=%v, height=%v at position=%v and color=%v\n", r.width, r.height, r.pos, r.color) 50 | } 51 | 52 | // Paint is implemented by Triangle 53 | func (c Triangle) Paint() { 54 | fmt.Printf("Painting triangle with p1=%v, p2=%v, p3=%v at position=%v and color=%v\n", c.p1, c.p2, c.p3, c.pos, c.color) 55 | } 56 | 57 | func main() { 58 | // Polymorph slice of Painter objects 59 | objects := []Painter{ 60 | // short initialization 61 | Circle{GeoObject{Position{1, 2}, 3}, 40}, 62 | Rectangle{GeoObject{Position{1, 2}, 4}, 10, 10}, 63 | Triangle{GeoObject{Position{1, 2}, 3}, Position{10, 20}, Position{11, 21}, Position{12, 22}}, 64 | 65 | // or with named identifiers 66 | Circle{ 67 | GeoObject: GeoObject{ 68 | pos: Position{x: 1, y: 2}, 69 | color: 3}, 70 | radius: 40}, 71 | Rectangle{ 72 | GeoObject: GeoObject{ 73 | pos: Position{x: 1, y: 2}, 74 | color: 4}, 75 | width: 10, 76 | height: 10}, 77 | Triangle{ 78 | GeoObject: GeoObject{ 79 | pos: Position{x: 1, y: 2}, 80 | color: 3}, 81 | p1: Position{x: 10, y: 20}, 82 | p2: Position{x: 11, y: 21}, 83 | p3: Position{x: 12, y: 22}}, 84 | } 85 | for _, v := range objects { 86 | v.Paint() 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /oop/polymorphism/polymorphism.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Point is a two dimensional point in a cartesian coordinate system. 8 | type Point struct{ x, y int } 9 | 10 | // Point implements the fmt.Stringer interface. 11 | func (p Point) String() string { 12 | return fmt.Sprintf("x=%v,y=%v", p.x, p.y) 13 | } 14 | 15 | // ColorPoint extends Point by adding a color field. 16 | type ColorPoint struct { 17 | Point // Embedding simulates inheritance but it is delegation! 18 | c int 19 | } 20 | 21 | // ColorPoint implements the fmt.Stringer interface. 22 | func (p ColorPoint) String() string { 23 | return fmt.Sprintf("x=%v,y=%v,c=%v", p.x, p.y, p.c) 24 | // OR: return fmt.Sprintf("%v,c=%v", p.Point, p.c) // Delegate to Point.String() 25 | } 26 | 27 | // END1 OMIT 28 | 29 | func main() { 30 | var p = Point{1, 2} 31 | var cp = ColorPoint{Point{1, 2}, 3} 32 | 33 | fmt.Println(p) 34 | fmt.Println(cp) 35 | fmt.Println(cp.x) // access inherited field 36 | 37 | // p = cp // does not work: No type hierarchy, no polymorphism 38 | p = cp.Point // works 39 | 40 | // s is a interface and supports Polymorphism 41 | var s fmt.Stringer 42 | s = p 43 | fmt.Println(s.String()) 44 | s = cp 45 | fmt.Println(s.String()) 46 | } 47 | 48 | // END2 OMIT 49 | -------------------------------------------------------------------------------- /oop/rational/rational.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend, Johannes Siedersleben 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | // Package rational implements rational numbers. 5 | package rational 6 | 7 | import "fmt" 8 | 9 | // Rational represents a rational number numerator/denominator. 10 | type Rational struct { 11 | numerator int 12 | denominator int 13 | } 14 | 15 | // END1 OMIT 16 | 17 | // NewRational constructor function 18 | func NewRational(numerator int, denominator int) Rational { 19 | if denominator == 0 { 20 | panic("division by zero") 21 | } 22 | r := Rational{} 23 | divisor := gcd(numerator, denominator) 24 | r.numerator = numerator / divisor 25 | r.denominator = denominator / divisor 26 | return r 27 | } 28 | 29 | // END2 OMIT 30 | 31 | // Multiply method for rational numbers (x1/x2 * y1/y2) 32 | func (x Rational) Multiply(y Rational) Rational { 33 | return NewRational(x.numerator*y.numerator, x.denominator*y.denominator) 34 | } 35 | 36 | // Multiply OMIT 37 | 38 | // Add adds two rational numbers 39 | func (x Rational) Add(y Rational) Rational { 40 | return NewRational(x.numerator*y.denominator+y.numerator*x.denominator, x.denominator*y.denominator) 41 | } 42 | 43 | // Stringer 44 | func (x Rational) String() string { 45 | return fmt.Sprintf("(%v/%v)", x.numerator, x.denominator) 46 | } 47 | 48 | // Stringer OMIT 49 | 50 | // Helper GCD -> Euclidean algorithm 51 | func gcd(x, y int) int { 52 | for y != 0 { 53 | x, y = y, x%y 54 | } 55 | return x 56 | } 57 | -------------------------------------------------------------------------------- /oop/rational/rational_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend, Johannes Siedersleben 2 | // Licensed under the Apache License, Version 2.0 3 | 4 | package rational 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | ) 10 | 11 | func TestRational(t *testing.T) { 12 | 13 | r1 := NewRational(1, 2) 14 | r2 := NewRational(2, 4) 15 | 16 | // test equal 17 | if r1 != r2 { 18 | t.Error("1/2 should be equal to 2/4 but is not.") 19 | } 20 | 21 | // test multiply 22 | r3 := r1.Multiply(r2) 23 | if r3 != NewRational(1, 4) { 24 | t.Error(fmt.Sprintf("1/2 * 1/2 should be 1/4 but ist %v", r3)) 25 | } 26 | 27 | // test add 28 | r4 := r3.Add(r3) 29 | if r4 != NewRational(1, 2) { 30 | t.Error(fmt.Sprintf("1/4 + 1/4 should be 1/2 but ist %v", r4)) 31 | } 32 | 33 | s:= fmt.Sprintf("x%vx", r4) 34 | if s != "x(1/2)x" { 35 | t.Error(fmt.Sprintf("Expected x(1/2)x but got: %s", s)) 36 | } 37 | 38 | } 39 | 40 | // END OMIT 41 | -------------------------------------------------------------------------------- /oop/stack/stack.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //Package stack contains LIFO functions. 16 | package stack 17 | 18 | // Stack is a generic LIFO container for untyped object. 19 | type Stack struct { 20 | data []interface{} 21 | } 22 | 23 | // NewStack constructs an empty stack. 24 | func NewStack() *Stack { 25 | return new(Stack) 26 | } 27 | 28 | // Push pushes a value on the stack. 29 | func (s *Stack) Push(value interface{}) { 30 | s.data = append(s.data, value) 31 | } 32 | 33 | // Pop pops a value from the stack. It returns an error if the stack is empty. 34 | func (s *Stack) Pop() interface{} { 35 | if len(s.data) == 0 { 36 | panic("can not pop: empty stack") 37 | } 38 | var result = s.data[len(s.data)-1] 39 | s.data = s.data[0 : len(s.data)-1] 40 | return result 41 | } 42 | 43 | // Size returns the count of elements in the Stack 44 | func (s *Stack) Size() int { 45 | return len(s.data) 46 | } 47 | 48 | // Get returns the n-th element in the Stack 49 | func (s *Stack) Get(idx int) interface{} { 50 | return s.data[idx] 51 | } 52 | 53 | // END OMIT 54 | -------------------------------------------------------------------------------- /oop/stack/stack_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend, Johannes Siedersleben 2 | // Licensed under the Apache License, Version 2.0 3 | package stack 4 | 5 | import ( 6 | "fmt" 7 | "testing" 8 | 9 | "github.com/jweigend/concepts-of-programming-languages/oop/rational" 10 | ) 11 | 12 | func TestStack(t *testing.T) { 13 | 14 | s := NewStack() 15 | 16 | s.Push("1") 17 | s.Push("2") 18 | s.Push("3") 19 | 20 | if s.Pop() != "3" { 21 | t.Error("Pop() did not return 3") 22 | } 23 | 24 | if s.Pop() != "2" { 25 | t.Error("Pop() did not return 2") 26 | } 27 | 28 | if s.Pop() != "1" { 29 | t.Error("Pop() did not return 1") 30 | } 31 | 32 | defer func() { 33 | if r := recover(); r != nil { 34 | fmt.Println("recovered:", r) 35 | } 36 | }() 37 | 38 | if s.Pop() != nil { 39 | t.Error("Stack should be empty, but is not.") 40 | } 41 | 42 | r1 := rational.NewRational(1, 2) 43 | r2 := rational.NewRational(2, 4) 44 | 45 | s.Push(r1) 46 | s.Push(r2) 47 | 48 | if s.Pop() != r2 { 49 | t.Error("Pop() did not return r2") 50 | } 51 | 52 | } 53 | 54 | func TestCasting(t *testing.T) { 55 | 56 | s := NewStack() 57 | s.Push(1) 58 | s.Push(2) 59 | 60 | sum := 0 61 | for i := 0; i < s.Size(); i++ { 62 | sum += s.Get(i).(int) // type assertion = cast from interface{} to int 63 | } 64 | 65 | if sum != 3 { 66 | t.Error(fmt.Sprintf("Sum result should be 3 but is %v", sum)) 67 | } 68 | } 69 | 70 | // END OMIT 71 | -------------------------------------------------------------------------------- /sp/container.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | // Simple "Docker"/Container implementation by Liz Rice. 4 | 5 | package main 6 | 7 | import ( 8 | "io/ioutil" 9 | "log" 10 | "os" 11 | "os/exec" 12 | "path/filepath" 13 | "strconv" 14 | "syscall" 15 | ) 16 | 17 | // go run main.go run 18 | func main() { 19 | switch os.Args[1] { 20 | case "run": 21 | run() 22 | case "child": 23 | child() 24 | default: 25 | panic("help") 26 | } 27 | } 28 | 29 | func run() { 30 | log.Printf("Running %v \n", os.Args[1:]) 31 | 32 | cmd := exec.Command("/proc/self/exe", append([]string{"child"}, os.Args[2:]...)...) 33 | cmd.Stdin = os.Stdin 34 | cmd.Stdout = os.Stdout 35 | cmd.Stderr = os.Stderr 36 | cmd.SysProcAttr = &syscall.SysProcAttr{ 37 | // Does not compile on OSX 38 | Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS, 39 | Unshareflags: syscall.CLONE_NEWNS, 40 | } 41 | 42 | must(cmd.Run()) 43 | } 44 | 45 | func child() { 46 | log.Printf("Running %v \n", os.Args[1:]) 47 | 48 | //cg() 49 | 50 | cmd := exec.Command(os.Args[2], os.Args[3:]...) 51 | cmd.Stdin = os.Stdin 52 | cmd.Stdout = os.Stdout 53 | cmd.Stderr = os.Stderr 54 | 55 | // Does not compile on OSX or Windows 56 | must(syscall.Sethostname([]byte("container"))) 57 | must(syscall.Chroot("/opt/alpinefs")) // local fs 58 | must(os.Chdir("/")) 59 | must(syscall.Mount("proc", "proc", "proc", 0, "")) 60 | 61 | must(cmd.Run()) 62 | 63 | must(syscall.Unmount("proc", 0)) 64 | } 65 | 66 | func cg() { 67 | cgroups := "/sys/fs/cgroup/" 68 | pids := filepath.Join(cgroups, "pids") 69 | os.Mkdir(filepath.Join(pids, "container"), 0755) 70 | must(ioutil.WriteFile(filepath.Join(pids, "container/pids.max"), []byte("20"), 0700)) 71 | // Removes the new cgroup in place after the container exits 72 | must(ioutil.WriteFile(filepath.Join(pids, "container/notify_on_release"), []byte("1"), 0700)) 73 | must(ioutil.WriteFile(filepath.Join(pids, "container/cgroup.procs"), []byte(strconv.Itoa(os.Getpid())), 0700)) 74 | } 75 | 76 | func must(err error) { 77 | if err != nil { 78 | panic(err) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /sp/empty.go: -------------------------------------------------------------------------------- 1 | package sp 2 | 3 | // avoid empty package error on build platform Windows or OSX -------------------------------------------------------------------------------- /sp/samples/os/pid.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Johannes Weigend 2 | // Licensed under the Apache License, Version 2.0 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | pid := os.Getpid() 12 | fmt.Println("process id: ", pid) 13 | } 14 | -------------------------------------------------------------------------------- /sp/samples/syscall/empty.go: -------------------------------------------------------------------------------- 1 | package syscall 2 | 3 | // avoid empty package error on build platform Windows or OSX 4 | -------------------------------------------------------------------------------- /sp/samples/syscall/pid.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | // Copyright 2018 Johannes Weigend, Johannes Siedersleben 4 | // Licensed under the Apache License, Version 2.0 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | 10 | "golang.org/x/sys/unix" 11 | ) 12 | 13 | func main() { 14 | pid, _, _ := unix.Syscall(39, 0, 0, 0) 15 | fmt.Println("process id: ", pid) 16 | } 17 | --------------------------------------------------------------------------------