├── Section 7 ├── actor.json ├── decorator_pattern.go ├── monads.go └── observer_pattern_channels.go ├── Section 8 ├── multiply.go ├── api.go ├── api_test.go ├── multiply_test.go ├── iss_position_t.go └── iss_position.go ├── Section 4 ├── immutable_maps │ ├── immutable_maps │ ├── immutable_map.go │ ├── popcorn.go │ └── hamt.go ├── immutable_struct │ ├── immutable_struct.go │ └── movie │ │ └── movie.go ├── monoid.go └── using_const.go ├── Section 3 ├── overview_function_literals.go ├── lambda_expressions.go ├── anonymous_functions.go ├── invoking_function_directly.go ├── observer_pattern.go ├── rxgo_example.go └── map_filter_reduce.go ├── Section 1 ├── 1.2 │ ├── functions.go │ ├── variables.go │ └── structs.go ├── 1.3 │ ├── concurrency.go │ └── interfaces.go └── 1.4 │ └── oop.go ├── Section 6 ├── partial_functions.go └── currying.go ├── Section 5 ├── using_closures.go ├── emulating_generators.go ├── using_sync.go ├── lazy_evaluation.go └── short_circuit_evaluation.go ├── Section 2 ├── 2.4-Closures │ ├── example_2.go │ └── example_1.go ├── 2.2-Pure functions │ └── pure_functions.go ├── 2.1-Overview of HOF │ └── hof.go └── 2.3-Encapsulating state │ └── encapsulating_state.go ├── LICENSE └── README.md /Section 7/actor.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Forest Whitaker", 3 | "age": 57, 4 | "height": 188 5 | } -------------------------------------------------------------------------------- /Section 8/multiply.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func Multiply(x int, y int) int { 4 | return x * y 5 | } 6 | -------------------------------------------------------------------------------- /Section 4/immutable_maps/immutable_maps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Functional-Programming-in-Go/HEAD/Section 4/immutable_maps/immutable_maps -------------------------------------------------------------------------------- /Section 3/overview_function_literals.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | getNumberOfUsers := func() int { 9 | return 20 10 | } 11 | 12 | fmt.Println(getNumberOfUsers()) 13 | } 14 | -------------------------------------------------------------------------------- /Section 1/1.2/functions.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func min(x, y int) int { 8 | if x < y { 9 | return x 10 | } 11 | return y 12 | } 13 | 14 | func main() { 15 | minNumber := min(3, 20) 16 | fmt.Println(minNumber) 17 | } 18 | -------------------------------------------------------------------------------- /Section 4/immutable_struct/immutable_struct.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "functional-programming-in-go/4_immutability_and_monads/immutable_struct/movie" 6 | ) 7 | 8 | func main() { 9 | m := movie.NewMovie("The Last Stand", 2013) 10 | fmt.Println(m.name) 11 | fmt.Printf(m.String()) 12 | } 13 | -------------------------------------------------------------------------------- /Section 8/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Connection interface { 4 | isAuthenticated(username string) bool 5 | } 6 | 7 | type API struct { 8 | conn Connection 9 | } 10 | 11 | func (a API) isAuthenticated(username string) bool { 12 | if a.conn.isAuthenticated(username) { 13 | return true 14 | } 15 | 16 | return false 17 | } 18 | -------------------------------------------------------------------------------- /Section 6/partial_functions.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func addSome(x, y, z int) int { 6 | return x + y + z 7 | } 8 | 9 | func addBy(x int) func(int, int) int { 10 | return func(y, z int) int { 11 | return addSome(x, y, z) 12 | } 13 | } 14 | 15 | func main() { 16 | addBy10 := addBy(10) 17 | fmt.Println(addBy10(4, 6)) // 20 18 | } 19 | -------------------------------------------------------------------------------- /Section 5/using_closures.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func increase() func() int { 8 | i := 0 9 | return func() int { 10 | i++ 11 | return i 12 | } 13 | } 14 | 15 | func main() { 16 | increment := increase() 17 | 18 | fmt.Println(increment()) // 1 19 | fmt.Println(increment()) // 2 20 | fmt.Println(increment()) // 3 21 | } 22 | -------------------------------------------------------------------------------- /Section 2/2.4-Closures/example_2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | clicker := newClick() 9 | fmt.Println(clicker()) // nrClicks = 1 10 | fmt.Println(clicker()) // nrClicks = 2 11 | } 12 | 13 | func newClick() func() int { 14 | nrClicks := 0 15 | return func() int { 16 | nrClicks++ 17 | return nrClicks 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Section 5/emulating_generators.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func fib(n int) chan int { 6 | c := make(chan int) 7 | go func() { 8 | x, y := 0, 1 9 | for i := 0; i <= n; i++ { 10 | c <- x 11 | x, y = y, x+y 12 | } 13 | close(c) 14 | }() 15 | return c 16 | } 17 | 18 | func main() { 19 | for i := range fib(3) { 20 | fmt.Println(i) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Section 2/2.4-Closures/example_1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | func main() { 9 | ages := []int{20, 16, 18, 14, 10, 20, 24, 45, 50, 35, 36} 10 | sort.Ints(ages) 11 | fmt.Println("sorted ages: ", ages) 12 | 13 | index := sort.Search(len(ages), func(i int) bool { 14 | return ages[i] >= 18 15 | }) 16 | 17 | fmt.Println(">= 18 years old:", ages[index:]) 18 | } 19 | -------------------------------------------------------------------------------- /Section 4/immutable_maps/immutable_map.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | // setting up the key:value pairs 9 | key1 := []byte("name") 10 | v1 := "Forest Whitaker" 11 | 12 | // new hashmap 13 | hashMap := New() 14 | hashMap.Insert(key1, v1) 15 | 16 | // getting values by key 17 | mapValue, _ := hashMap.Get(key1) 18 | fmt.Println(mapValue) 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Section 4/monoid.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | var identity string = "" 8 | var mappend = func(a, b string) string { 9 | return a + b 10 | } 11 | 12 | func main() { 13 | strings := []string{"Forest", " Whitaker", ", 2018"} 14 | 15 | var concatenated = identity 16 | 17 | for _, s := range strings { 18 | concatenated = mappend(concatenated, s) 19 | } 20 | fmt.Println(concatenated) 21 | } 22 | -------------------------------------------------------------------------------- /Section 3/lambda_expressions.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func areaOfCircle(radius float64) float64 { 6 | area := 3.14159 * radius * radius 7 | return area 8 | } 9 | 10 | func main() { 11 | // emulating a lambda expression using an anonymous function 12 | area := func(x float64) float64 { return areaOfCircle(x) } 13 | 14 | fmt.Println(area(10)) 15 | 16 | // adding two numbers 17 | add := func(x, y float64) float64 { return x + y } 18 | fmt.Println(add(3, 4)) 19 | } 20 | -------------------------------------------------------------------------------- /Section 4/immutable_struct/movie/movie.go: -------------------------------------------------------------------------------- 1 | package movie 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type movie struct { 8 | name string // non-exported 9 | releaseYear uint // non-exported 10 | } 11 | 12 | // initialize 13 | func NewMovie(name string, releaseYear uint) *movie { 14 | m := &movie{ 15 | name: name, 16 | releaseYear: releaseYear, 17 | } 18 | 19 | return m 20 | } 21 | 22 | func (m *movie) String() string { 23 | return fmt.Sprintf("%s was released in %d.\n", m.name, m.releaseYear) 24 | } 25 | -------------------------------------------------------------------------------- /Section 1/1.3/concurrency.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func sum(a []int, c chan int) { 6 | sum := 0 7 | for _, v := range a { 8 | sum += v 9 | } 10 | c <- sum // send sum to channel c 11 | } 12 | 13 | func main() { 14 | numbers := []int{1, 2, 3, 4, 5, 6} 15 | 16 | c := make(chan int) 17 | go sum(numbers[:len(numbers)/2], c) // goroutine 1 18 | go sum(numbers[len(numbers)/2:], c) // goroutine 2 19 | x, y := <-c, <-c // receive x, y from channel c 20 | 21 | fmt.Println(x, y, x+y) 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Section 1/1.2/variables.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // read only variables 8 | const ( 9 | eulerConst = 2.71828 // eulers constant 10 | ) 11 | 12 | // file block variable declaration 13 | var ( 14 | isProtagonist = true 15 | isFast = true 16 | ) 17 | 18 | func main() { 19 | name := "Dominic" // short variable declaration 20 | var lastname string = "Toretto" 21 | var age = 42 22 | 23 | var friend string // declare 24 | friend = "Brian" // and initialize 25 | fmt.Println(name, lastname, age, friend, eulerConst, isProtagonist) 26 | } 27 | -------------------------------------------------------------------------------- /Section 2/2.2-Pure functions/pure_functions.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | func isMinor(age int) bool { 11 | if age >= 18 { 12 | return false 13 | } 14 | 15 | return true 16 | } 17 | 18 | func main() { 19 | rand.Seed(time.Now().UTC().UnixNano()) 20 | fmt.Printf("isMinor(20): %v\n", isMinor(20)) // pure 21 | fmt.Printf("rand.Intn(10): %v\n", rand.Intn(10)) // impure 22 | 23 | square := math.Sqrt(4) // pure 24 | fmt.Printf("math.Sqrt(4): %v\n", square) // impure (fmt.Printf) 25 | } 26 | -------------------------------------------------------------------------------- /Section 4/using_const.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | const untypeC = 3 8 | 9 | const ( 10 | pi float64 = 3.14159 11 | ) 12 | 13 | func main() { 14 | // untyped 15 | var typedFloat float64 = untypeC 16 | fmt.Println(typedFloat) 17 | 18 | // pi = 3.15 // error: cannot assign to PI 19 | fmt.Println(pi) 20 | 21 | const firstname = "Forest" 22 | const lastname = "Whitaker" 23 | 24 | const name = firstname + " " + lastname 25 | 26 | const newFirstname = "Joe" 27 | const newName = newFirstname + " " + lastname 28 | 29 | fmt.Println(name, newName) 30 | } 31 | -------------------------------------------------------------------------------- /Section 5/using_sync.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func getRemoteData(ms time.Duration, wg *sync.WaitGroup) { 10 | defer wg.Done() 11 | 12 | duration := ms * time.Millisecond 13 | time.Sleep(duration) 14 | fmt.Println("retrieving data in : ", duration) 15 | } 16 | 17 | func main() { 18 | var wg sync.WaitGroup 19 | 20 | wg.Add(4) 21 | go getRemoteData(1000, &wg) 22 | go getRemoteData(800, &wg) 23 | go getRemoteData(650, &wg) 24 | go getRemoteData(100, &wg) 25 | 26 | wg.Wait() 27 | fmt.Println("finished getting all the data") 28 | } 29 | -------------------------------------------------------------------------------- /Section 5/lazy_evaluation.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | type LazyInt func() int 9 | 10 | func Make(f func() int) LazyInt { 11 | var v int 12 | var once sync.Once 13 | 14 | return func() int { 15 | once.Do(func() { 16 | v = f() 17 | f = nil // garbage collect f 18 | }) 19 | return v 20 | } 21 | } 22 | 23 | func main() { 24 | n := Make(func() int { return 20 }) // different computations 25 | fmt.Println(n()) // calculates 20 26 | // reused n, the calculated value, it doesnt compute it again 27 | fmt.Println(n() + 40) 28 | } 29 | -------------------------------------------------------------------------------- /Section 5/short_circuit_evaluation.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func isAuthenticated() bool { 8 | fmt.Println("isAuthenticated()") 9 | return false 10 | } 11 | 12 | func isAdmin() bool { 13 | fmt.Println("isAdmin()") 14 | return true 15 | } 16 | 17 | func main() { 18 | if isAuthenticated() && isAdmin() { 19 | fmt.Println("is authenticated and admin") 20 | } 21 | 22 | if isAuthenticated() || isAdmin() { 23 | fmt.Println("is authenticated or admin") 24 | } 25 | 26 | if isAdmin() || isAuthenticated() { 27 | fmt.Println("admin or authenticated") 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /Section 8/api_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | type MockedConn struct { 11 | mock.Mock 12 | } 13 | 14 | func (m *MockedConn) isAuthenticated(username string) bool { 15 | args := m.Called(username) 16 | return args.Bool(0) 17 | 18 | } 19 | 20 | func TestAPI(t *testing.T) { 21 | conn := new(MockedConn) 22 | api := API{ 23 | conn: conn, 24 | } 25 | 26 | conn.On("isAuthenticated", "forest.whitaker").Return(false) 27 | 28 | assert.Equal(t, api.isAuthenticated("forest.whitaker"), true) 29 | } 30 | -------------------------------------------------------------------------------- /Section 7/decorator_pattern.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type stringModifier func(string) string 9 | 10 | func toLowercase(sm stringModifier) stringModifier { 11 | return func(s string) string { 12 | lower := strings.ToLower(s) 13 | return sm(lower) 14 | } 15 | } 16 | 17 | func main() { 18 | name := "Forest Whitaker" 19 | 20 | var fn stringModifier = func(s string) string { 21 | return s 22 | } 23 | fmt.Println(fn(name)) // default value 24 | 25 | // using the decorator 26 | fn = toLowercase(fn) 27 | fmt.Println(fn(name)) // output default values again ? 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Section 8/multiply_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestMultiply(t *testing.T) { 8 | total := Multiply(5, 5) 9 | if total != 25 { 10 | t.Errorf("Multiply was incorrect, got: %d, want: %d.", total, 10) 11 | } 12 | } 13 | 14 | func TestMultiplyTable(t *testing.T) { 15 | tables := []struct { 16 | x int 17 | y int 18 | m int 19 | }{ 20 | {1, 1, 1}, 21 | {1, 2, 2}, 22 | {2, 2, 4}, 23 | {5, 2, 10}, 24 | } 25 | 26 | for _, table := range tables { 27 | total := Multiply(table.x, table.y) 28 | if total != table.m { 29 | t.Errorf("%d*%d != %d, but is %d", table.x, table.y, table.m, total) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Section 2/2.1-Overview of HOF/hof.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func showName(f func(string), name string) { 6 | f(name) 7 | } 8 | 9 | func printInConsole(name string) { 10 | fmt.Printf("The name is %s.\n", name) 11 | } 12 | 13 | // a function (getClicker) that returns a function (which returns an int) 14 | func getClicker() func() int { 15 | i := 0 16 | return func() int { 17 | i++ 18 | return i 19 | } 20 | } 21 | 22 | func main() { 23 | // 1. function as parameter 24 | showName(printInConsole, "Forest") 25 | 26 | // 2. returning a function 27 | click := getClicker() 28 | fmt.Println(click()) // 1 29 | fmt.Println(click()) // 2 30 | fmt.Println(click()) // 3 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Section 1/1.2/structs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type actor struct { 9 | protagonist bool 10 | person person // embedded type 11 | } 12 | 13 | type person struct { 14 | firstname string 15 | lastname string 16 | age int 17 | dob time.Time 18 | } 19 | 20 | func main() { 21 | p := person{"Dominic", "Toretto", 42, time.Date(1976, time.August, 29, 0, 0, 0, 0, time.UTC)} 22 | a := actor{true, p} 23 | 24 | // fmt.Println(a) 25 | fmt.Printf("Firstname is %s. \n", a.person.firstname) 26 | fmt.Printf("Date of birth is %s. \n", a.person.dob.Format("2006-01-02")) 27 | 28 | // anonymous struct 29 | fmt.Println(struct { 30 | age int 31 | }{42}) 32 | } 33 | -------------------------------------------------------------------------------- /Section 1/1.3/interfaces.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type actor struct { 8 | protagonist bool 9 | } 10 | 11 | func (a actor) talk() string { 12 | return "Actor is talking" 13 | } 14 | 15 | type driver struct { 16 | isGood bool 17 | } 18 | 19 | func (d driver) talk() string { 20 | return "Driver is talking" 21 | } 22 | 23 | // to be used by actor and driver 24 | // we define 'methods sets' inside the interface 25 | // instead of fields 26 | type humanizer interface { 27 | talk() string 28 | } 29 | 30 | func talkAll(h []humanizer) { 31 | for _, v := range h { 32 | fmt.Println(v.talk()) 33 | } 34 | 35 | } 36 | 37 | func main() { 38 | a := actor{true} 39 | d := driver{true} 40 | 41 | h := []humanizer{a, d} 42 | talkAll(h) 43 | } 44 | -------------------------------------------------------------------------------- /Section 8/iss_position_t.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | "net/http" 6 | "net/http/httptest" 7 | "testing" 8 | ) 9 | 10 | func TestGetISSLocation(t *testing.T) { 11 | req, err := http.NewRequest("GET", "http://api.open-notify.org/iss-now.json", nil) 12 | req.Header.Set("Content-Type", "application/json2") 13 | 14 | if err != nil { 15 | t.Fatal(err) 16 | } 17 | 18 | resp := httptest.NewRecorder() 19 | http.DefaultServeMux.ServeHTTP(resp, req) 20 | 21 | // test Content-Type, StatusCode 22 | if req.Header.Get("Content-Type") != "application/json" { 23 | t.Fatalf("Received non JSON response: %s\n", req.Header.Get("Content-Type")) 24 | 25 | } 26 | 27 | _, err = ioutil.ReadAll(resp.Body) 28 | 29 | if err != nil { 30 | t.Fail() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Section 3/anonymous_functions.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | rand.Seed(time.Now().UTC().UnixNano()) 12 | 13 | // anonymous function 14 | anon := func() { 15 | fmt.Println("I am an anonymous function") 16 | } 17 | anon() 18 | 19 | // self invoked anonymous function 20 | func() { 21 | fmt.Println("I am a self-invoked anonymous function") 22 | }() 23 | 24 | // multiple anonymous functions 25 | x := 10 26 | functions := []func(){ 27 | func() { x += 5 }, 28 | func() { x -= 5 }, 29 | func() { x /= 2 }, 30 | func() { x *= 5 }, 31 | } 32 | getRandomFunc(functions)() // change value of x 33 | log.Println(x) 34 | 35 | } 36 | 37 | func getRandomFunc(functions []func()) func() { 38 | return functions[rand.Intn(len(functions))] 39 | } 40 | -------------------------------------------------------------------------------- /Section 2/2.3-Encapsulating state/encapsulating_state.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "time" 7 | 8 | "github.com/go-functional/core/functor" 9 | ) 10 | 11 | func main() { 12 | rand.Seed(time.Now().UnixNano()) 13 | 14 | firstNumber := getInt() 15 | secondNumber := getInt() 16 | log.Printf("created numbers %s and %s", firstNumber, secondNumber) 17 | 18 | intMapperFunc := func(i int) int { 19 | return i + 10 20 | } 21 | mappedOptionalInt1 := firstNumber.Map(intMapperFunc) 22 | mappedOptionalInt2 := secondNumber.Map(intMapperFunc) 23 | 24 | log.Printf("mapped optional ints %s and %s", mappedOptionalInt1, mappedOptionalInt2) 25 | 26 | } 27 | 28 | func getInt() functor.OptionalIntFunctor { 29 | if rand.Int()%2 == 0 { 30 | return functor.SomeInt(rand.Int()) 31 | } 32 | return functor.EmptyInt() 33 | } 34 | -------------------------------------------------------------------------------- /Section 3/invoking_function_directly.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | age := func() int { 9 | return 10 10 | }() // declare and invoke at the same time 11 | 12 | fmt.Printf("age old scope: %d \n", age) 13 | 14 | // age := 20 // error: no new variables on left side of := 15 | 16 | // creating their own scope 17 | func() { 18 | age := 20 19 | fmt.Printf("age new scope: %d \n", age) 20 | 21 | // creating a struct inside an anonymous function 22 | type actor struct{ name string } 23 | a := actor{"Forest Whitaker"} 24 | fmt.Println(a) 25 | }() 26 | 27 | // garbage collection friendly 28 | func() { 29 | numbers := make([]int, 0) 30 | for i := 1; i <= 1000; i++ { 31 | numbers = append(numbers, i) 32 | } 33 | // fmt.Println(numbers) 34 | }() // function is finished executing 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Section 6/currying.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | func inPowerN(b float64) func(float64) float64 { 9 | return func(e float64) float64 { return math.Pow(b, e) } 10 | } 11 | 12 | type MethodE int 13 | 14 | func (m MethodE) Method(b int) int { 15 | return int(m) + b 16 | } 17 | 18 | func main() { 19 | // currying math.Pow function 20 | twoInPowerOf := inPowerN(2) 21 | twoInPowerOfThree := twoInPowerOf(3) 22 | fmt.Println("2^3 =", twoInPowerOfThree) 23 | 24 | var m MethodE = 5 25 | 26 | // currying using method value 27 | curr := m.Method 28 | 29 | // uncurrying using method expressions 30 | uncurr := MethodE.Method 31 | 32 | fmt.Println("5 + 1 =", m.Method(1)) // standard 33 | 34 | fmt.Println("5 + 2 =", curr(2)) 35 | 36 | fmt.Println("5 + 3 =", uncurr(m, 3)) 37 | fmt.Println("5 + 4 =", uncurr(MethodE(5), 4)) 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Section 3/observer_pattern.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | //Listener is a 6 | type Listener struct { 7 | ID int 8 | } 9 | 10 | //ListenerInterface is an 11 | type ListenerInterface interface { 12 | execute(m string) 13 | } 14 | 15 | func (l *Listener) execute(m string) { 16 | fmt.Printf("%q message receiver for id %d \n", m, l.ID) 17 | } 18 | 19 | //Subject is an 20 | type Subject struct { 21 | listeners []ListenerInterface 22 | } 23 | 24 | //AddListener is a 25 | func (s *Subject) addListener(l ListenerInterface) { 26 | s.listeners = append(s.listeners, l) 27 | } 28 | 29 | func (s *Subject) notify(m string) { 30 | for _, l := range s.listeners { 31 | if l != nil { 32 | l.execute(m) 33 | } 34 | } 35 | } 36 | 37 | var iter int 38 | 39 | func newListener() *Listener { 40 | l := Listener{iter} 41 | iter++ 42 | return &l 43 | } 44 | func main() { 45 | iter = 0 46 | s := Subject{listeners: make([]ListenerInterface, 0)} 47 | 48 | l := newListener() 49 | s.addListener(l) 50 | 51 | for i := 0; i < 5; i++ { 52 | l = newListener() 53 | s.addListener(l) 54 | } 55 | 56 | s.notify("Hello") 57 | s.notify("Goobye") 58 | } 59 | -------------------------------------------------------------------------------- /Section 8/iss_position.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "os" 9 | ) 10 | 11 | type Location struct { 12 | Position Position `json:"iss_position"` 13 | Message string `json:"message"` 14 | } 15 | 16 | type Position struct { 17 | Longitude string `json:"longitude"` 18 | Latitude string `json:"latitude"` 19 | } 20 | 21 | // get Internation Space Station Position 22 | func GetISSPosition() (*Location, error) { 23 | const url = "http://api.open-notify.org/iss-now.json" 24 | 25 | resp, err := http.Get(url) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | body, err := ioutil.ReadAll(resp.Body) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | l := Location{} 36 | err = json.Unmarshal(body, &l) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | return &l, nil 42 | 43 | } 44 | 45 | func main() { 46 | resp, err := GetISSPosition() 47 | if err != nil { 48 | fmt.Println(err.Error()) 49 | os.Exit(1) 50 | } 51 | fmt.Printf("Longitude: %s\n", resp.Position.Longitude) 52 | fmt.Printf("Latitude: %s\n", resp.Position.Latitude) 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Section 3/rxgo_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "strings" 9 | 10 | "github.com/reactivex/rxgo/handlers" 11 | "github.com/reactivex/rxgo/observable" 12 | ) 13 | 14 | func main() { 15 | var num1, num2 int 16 | reader := bufio.NewReader(os.Stdin) 17 | 18 | processText := func(text, prefix string, numPtr *int) { 19 | text = strings.Trim(strings.TrimPrefix(text, prefix), " \n") 20 | *numPtr, _ = strconv.Atoi(text) 21 | } 22 | 23 | // All side effects are consolidated into this handler. 24 | onNext := handlers.NextFunc(func(item interface{}) { 25 | if text, ok := item.(string); ok { 26 | switch { 27 | case strings.HasPrefix(text, "a="): 28 | processText(text, "a=", &num1) 29 | case strings.HasPrefix(text, "b="): 30 | processText(text, "b=", &num2) 31 | default: 32 | fmt.Println("Input does not start with prefix \"a=\" or \"b=\"!") 33 | return 34 | } 35 | } 36 | 37 | fmt.Printf("The sum (%d + %d) = %d\n", num1, num2, num1+num2) 38 | 39 | }) 40 | 41 | for { 42 | fmt.Print("Modify a/b: ") 43 | 44 | sub := observable.Start(func() interface{} { 45 | text, err := reader.ReadString('\n') 46 | if err != nil { 47 | return err 48 | } 49 | return text 50 | }).Subscribe(onNext) 51 | 52 | <-sub 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Section 7/monads.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "log" 7 | ) 8 | 9 | type Any interface{} 10 | 11 | type Monad func(error) (Any, error) 12 | 13 | func Return(v Any) Monad { 14 | return func(s error) (Any, error) { 15 | return v, s 16 | } 17 | } 18 | 19 | func Bind(m Monad, f func(Any) Monad) Monad { 20 | return func(s error) (Any, error) { 21 | a, b := m(s) 22 | if b != nil { 23 | return nil, b 24 | } 25 | return f(a)(b) 26 | } 27 | } 28 | 29 | func ReadFile(filename Any) Monad { 30 | log.Println("reading the file") 31 | 32 | filenameString := filename.(string) 33 | return func(error) (Any, error) { 34 | return ioutil.ReadFile(filenameString) 35 | } 36 | } 37 | 38 | func ToJSON(v Any) Monad { 39 | log.Println("unmarshalling the file to json") 40 | 41 | vBytes := v.([]byte) 42 | return func(s error) (Any, error) { 43 | type actor struct { 44 | Name string `json:"name"` 45 | Age uint `json:"age"` 46 | Height uint `json:"height"` 47 | } 48 | a := actor{} 49 | err := json.Unmarshal(vBytes, &a) 50 | return a, err 51 | } 52 | } 53 | 54 | func main() { 55 | monad := Return("actor.json") 56 | 57 | monad = Bind(monad, ReadFile) 58 | monad = Bind(monad, ToJSON) 59 | 60 | actorJSON, err := monad(nil) 61 | if err != nil { 62 | log.Fatal(err) 63 | } 64 | log.Println(actorJSON) 65 | } 66 | -------------------------------------------------------------------------------- /Section 3/map_filter_reduce.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // returns the index of the number in the slice if found 6 | // otherwise returns -1 7 | func index(vs []int, t int) int { 8 | for i, v := range vs { 9 | if v == t { 10 | return i 11 | } 12 | } 13 | return -1 14 | } 15 | 16 | // returns true if number is in the slice 17 | func include(vs []int, t int) bool { 18 | return index(vs, t) >= 0 19 | } 20 | 21 | // filter returns a new slice 22 | // that satisfies function f() 23 | func Filter(vs []int, f func(int) bool) []int { 24 | vsf := make([]int, 0) 25 | for _, v := range vs { 26 | if f(v) { 27 | vsf = append(vsf, v) 28 | } 29 | } 30 | return vsf 31 | } 32 | 33 | // map returns a new slice after applying 34 | // function f into each element of the slice 35 | func Map(vs []int, f func(int) int) []int { 36 | vsm := make([]int, len(vs)) 37 | for i, v := range vs { 38 | vsm[i] = f(v) 39 | } 40 | return vsm 41 | } 42 | 43 | func main() { 44 | var weights = []int{60, 80, 64, 101, 92} // in kg 45 | 46 | fmt.Printf("the index of number 40 is: %d. \n", index(weights, 40)) 47 | fmt.Printf("number 80 exists in the slice: %v. \n", include(weights, 80)) 48 | 49 | // convert kg weights into pound 50 | fmt.Printf("in pounds -> %v. \n", Map(weights, func(w int) int { 51 | return w * 2 52 | })) 53 | 54 | // find numbers bigger than 80 55 | fmt.Printf("bigger than 80 -> %v. \n", Filter(weights, func(w int) bool { 56 | return w > 80 57 | })) 58 | } 59 | -------------------------------------------------------------------------------- /Section 1/1.4/oop.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type person struct { 9 | firstname string 10 | lastname string 11 | age int 12 | added time.Time 13 | } 14 | 15 | type Actor struct { 16 | isProtagonist bool 17 | person person // embedding 18 | } 19 | 20 | func (a *Actor) String() string { 21 | return fmt.Sprintf("%s %s is %d years old.", a.person.firstname, a.person.lastname, a.person.age) 22 | } 23 | 24 | func (a Actor) walk() string { 25 | return fmt.Sprintf("Actor: %s %s is walking", a.person.firstname, a.person.lastname) 26 | } 27 | 28 | type Director struct { 29 | firstname string 30 | lastname string 31 | hasNobelPrice bool 32 | } 33 | 34 | func (d Director) walk() string { 35 | return fmt.Sprintf("Director: %s %s is walking", d.firstname, d.lastname) 36 | } 37 | 38 | type walker interface { 39 | walk() string 40 | } 41 | 42 | // emulating a constructor using New() 43 | func New(fn, ln string, age int, isProtagonist bool) *Actor { 44 | return &Actor{ 45 | isProtagonist: true, 46 | person: person{ 47 | firstname: fn, 48 | lastname: ln, 49 | age: age, 50 | added: time.Now(), 51 | }, 52 | } 53 | } 54 | 55 | func main() { 56 | // this is supposed to be a package 57 | // so it is called actor.New() 58 | actor := New("Dom", "Toretto", 42, true) // new instance of Actor 59 | fmt.Println(actor.String()) 60 | 61 | director := Director{"John", "Doe", false} 62 | 63 | // using interfaces 64 | walker := []walker{actor, director} 65 | 66 | for _, v := range walker { 67 | fmt.Println(v.walk()) 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /Section 4/immutable_maps/popcorn.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Hamming weight pulled from wikipedia 4 | // http://en.wikipedia.org/wiki/Hamming_weight 5 | // this includes the crazy uint64s and popcount 6 | const ( 7 | m1 = 0x5555555555555555 //binary: 0101... 8 | m2 = 0x3333333333333333 //binary: 00110011.. 9 | m4 = 0x0f0f0f0f0f0f0f0f //binary: 4 zeros, 4 ones ... 10 | m8 = 0x00ff00ff00ff00ff //binary: 8 zeros, 8 ones ... 11 | m16 = 0x0000ffff0000ffff //binary: 16 zeros, 16 ones ... 12 | m32 = 0x00000000ffffffff //binary: 32 zeros, 32 ones 13 | hff = 0xffffffffffffffff //binary: all ones 14 | h01 = 0x0101010101010101 //the sum of 256 to the power of 0,1,2,3... 15 | ) 16 | 17 | //This uses fewer arithmetic operations than any other known 18 | //implementation on machines with slow multiplication. 19 | //It uses 17 arithmetic operations. 20 | func popcount_2(x uint64) uint { 21 | x -= (x >> 1) & m1 //put count of each 2 bits into those 2 bits 22 | x = (x & m2) + ((x >> 2) & m2) //put count of each 4 bits into those 4 bits 23 | x = (x + (x >> 4)) & m4 //put count of each 8 bits into those 8 bits 24 | x += x >> 8 //put count of each 16 bits into their lowest 8 bits 25 | x += x >> 16 //put count of each 32 bits into their lowest 8 bits 26 | x += x >> 32 //put count of each 64 bits into their lowest 8 bits 27 | return uint(x & 0x7f) 28 | } 29 | 30 | //This uses fewer arithmetic operations than any other known 31 | //implementation on machines with fast multiplication. 32 | //It uses 12 arithmetic operations, one of which is a multiply. 33 | func popcount_3(x uint64) uint { 34 | x -= (x >> 1) & m1 //put count of each 2 bits into those 2 bits 35 | x = (x & m2) + ((x >> 2) & m2) //put count of each 4 bits into those 4 bits 36 | x = (x + (x >> 4)) & m4 //put count of each 8 bits into those 8 bits 37 | return uint((x * h01) >> 56) //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ... 38 | } 39 | -------------------------------------------------------------------------------- /Section 7/observer_pattern_channels.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type Broker struct { 9 | stopCh chan struct{} 10 | publishCh chan interface{} 11 | subscribeCh chan chan interface{} 12 | unsubscribeCh chan chan interface{} 13 | } 14 | 15 | func NewBroker() *Broker { 16 | return &Broker{ 17 | stopCh: make(chan struct{}), 18 | publishCh: make(chan interface{}, 1), 19 | subscribeCh: make(chan chan interface{}, 1), 20 | unsubscribeCh: make(chan chan interface{}, 1), 21 | } 22 | } 23 | 24 | func (b *Broker) Start() { 25 | subs := map[chan interface{}]struct{}{} 26 | for { 27 | select { 28 | case <-b.stopCh: 29 | return 30 | case msgCh := <-b.subscribeCh: 31 | subs[msgCh] = struct{}{} 32 | case msgCh := <-b.unsubscribeCh: 33 | delete(subs, msgCh) 34 | case msg := <-b.publishCh: 35 | for msgCh := range subs { 36 | select { 37 | case msgCh <- msg: 38 | default: 39 | } 40 | } 41 | } 42 | } 43 | } 44 | 45 | func (b *Broker) Stop() { 46 | close(b.stopCh) 47 | } 48 | 49 | func (b *Broker) Subscribe() chan interface{} { 50 | msgCh := make(chan interface{}, 5) 51 | b.subscribeCh <- msgCh 52 | return msgCh 53 | } 54 | 55 | func (b *Broker) Unsubscribe(msgCh chan interface{}) { 56 | b.unsubscribeCh <- msgCh 57 | } 58 | 59 | func (b *Broker) Publish(msg interface{}) { 60 | b.publishCh <- msg 61 | } 62 | 63 | func main() { 64 | b := NewBroker() 65 | go b.Start() 66 | 67 | // create and subscribe 3 users: 68 | userReceiveNotifications := func(id int) { 69 | msgCh := b.Subscribe() 70 | for { 71 | fmt.Printf("User %d got notification: %v\n", (id + 1), <-msgCh) 72 | } 73 | } 74 | for i := 0; i < 3; i++ { 75 | go userReceiveNotifications(i) 76 | } 77 | 78 | // publish the notifications: 79 | go func() { 80 | for notificationID := 0; ; notificationID++ { 81 | b.Publish(fmt.Sprintf("number: %d", notificationID)) 82 | time.Sleep(200 * time.Millisecond) 83 | } 84 | }() 85 | 86 | time.Sleep(time.Second) 87 | } 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Functional Programming in Go [Video] 2 | This is the code repository for [Functional Programming in Go [Video]](https://www.packtpub.com/application-development/functional-programming-go-video?utm_source=github&utm_medium=repository&utm_campaign=9781787283480), published by [Packt](https://www.packtpub.com/?utm_source=github). It contains all the supporting project files necessary to work through the video course from start to finish. 3 | ## About the Video Course 4 | Functional Programming in Go aims to demonstrate existing Go developers how to write more reliable code based on the principles of Functional Programming. We will start with a quick refresher in Go, continuing in showing the ins and outs of functions, higher order functions as well explaining the benefits of immutability, currying and design patterns with real world examples. In the end we will have a look at unit testing using error and assertion based approaches. 5 | 6 | We will explain abstract mathematical terms like Monoids, Monads and Functors in a simple and practical way used in Programming. Besides that we will have a look at the advantages and disadvantages at all principles backed by arguments and examples.This course is ideal for anybody already familiar with Go and has used Go mostly with Object Oriented Principles in mind and would like to start building reliable, concurrent apps in Go using Functional Programming. 7 | 8 | 9 |

What You Will Learn

10 |
11 |
20 | 21 | ## Instructions and Navigation 22 | ### Assumed Knowledge 23 | To fully benefit from the coverage included in this course, you will need:
24 | To fully benefit from the coverage included in this course, you will need: 25 | 26 | ● Prior working knowledge of Go 27 | 28 | ● (Optional) Familiar with a Functional Programming Language 29 | 30 | ### Technical Requirements 31 | This course has the following software requirements:
32 | This course has the following software requirements: 33 | 34 | ● An editor like Atom, Sublime Text or Visual Studio Code with respective Go plugins 35 | 36 | ● An IDE like Goland 37 | 38 | ● Go - Version 1.10 39 | 40 | This course has been tested on the following system configuration: 41 | 42 | ● OS: Ubuntu 16.04 43 | 44 | ● Processor: Intel i5 8th Gen 45 | 46 | ● Memory: 8GB 47 | 48 | ● Hard Disk Space: 200MB 49 | 50 | ● Video Card: 256MB Video Memory 51 | 52 | ## Related Products 53 | * [Hands-on Concurrency with Go [Video]](https://www.packtpub.com/application-development/hands-concurrency-go-video?utm_source=github&utm_medium=repository&utm_campaign=9781788993746) 54 | 55 | * [Troubleshooting Go Application Development [Video]](https://www.packtpub.com/application-development/troubleshooting-go-application-development-video?utm_source=github&utm_medium=repository&utm_campaign=9781788997072) 56 | 57 | * [Learn Go in 3 Hours [Video]](https://www.packtpub.com/application-development/learn-go-3-hours-video?utm_source=github&utm_medium=repository&utm_campaign=9781788992053) 58 | 59 | -------------------------------------------------------------------------------- /Section 4/immutable_maps/hamt.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "hash/fnv" 8 | ) 9 | 10 | const ( 11 | fanoutLog2 = 6 12 | fanout uint = 1 << fanoutLog2 13 | fanMask uint = fanout - 1 14 | maxDepth = 60 / fanoutLog2 15 | keyNotFound = "Key not found" 16 | ) 17 | 18 | type Key []byte 19 | 20 | type node interface { 21 | assoc(shift int, hash uint64, key Key, value interface{}) (last node, leaf *valueNode) 22 | without(shift int, hash uint64, key Key) node 23 | find(shift int, hash uint64, key Key) (value interface{}, err error) 24 | pos() uint64 25 | } 26 | 27 | type PersistentMap struct { 28 | root *bitmapNode 29 | // collision map[uint]interface{} 30 | } 31 | 32 | type valueNode struct { 33 | key Key 34 | hash uint64 35 | value interface{} 36 | bitpos uint64 37 | } 38 | 39 | type bitmapNode struct { 40 | childBitmap uint64 41 | children []node 42 | bitpos uint64 43 | } 44 | 45 | func (n *valueNode) assoc(shift int, hash uint64, key Key, val interface{}) (last node, leaf *valueNode) { 46 | if n.hash == hash { 47 | n.value = val 48 | last = n 49 | leaf = n 50 | } else { 51 | nn := &bitmapNode{0, make([]node, 2, 2), n.pos()} 52 | last = nn 53 | nn.assoc(shift, n.hash, key, n.value) 54 | _, leaf = nn.assoc(shift, hash, key, val) 55 | } 56 | 57 | return last, leaf 58 | } 59 | 60 | func (n *valueNode) without(shift int, hash uint64, key Key) node { 61 | return n 62 | } 63 | 64 | func (n *valueNode) find(shift int, hash uint64, key Key) (value interface{}, err error) { 65 | if hash == n.hash { 66 | value = n.value 67 | } else { 68 | err = errors.New(keyNotFound) 69 | } 70 | return value, err 71 | } 72 | 73 | func (n *valueNode) pos() uint64 { 74 | return n.bitpos 75 | } 76 | 77 | func (n *bitmapNode) assoc(shift int, hash uint64, key Key, val interface{}) (last node, leaf *valueNode) { 78 | bitsToShift := uint(shift * fanoutLog2) 79 | pos := bitpos(hash, bitsToShift) 80 | 81 | if (pos & n.childBitmap) == 0 { //nothing in slot, not found 82 | //mark our slot taken and xpand our children 83 | n.childBitmap |= pos 84 | newChildren := make([]node, (len(n.children) + 1)) 85 | 86 | newChildIndex := n.index(pos) 87 | newChild := &valueNode{key, hash, val, pos} 88 | newChildren[newChildIndex] = newChild 89 | 90 | for _, c := range n.children { 91 | if c != nil { 92 | oldChildNewIndex := n.index(c.pos()) 93 | newChildren[oldChildNewIndex] = c 94 | } 95 | } 96 | 97 | n.children = newChildren 98 | last = n 99 | leaf = newChild 100 | } else { 101 | index := n.index(pos) 102 | nodeAtIndex := n.children[index] 103 | last, leaf = nodeAtIndex.assoc(shift+1, hash, key, val) 104 | 105 | if _, isValNode := nodeAtIndex.(*valueNode); isValNode { 106 | n.children[index] = last 107 | } 108 | } 109 | return last, leaf 110 | } 111 | 112 | func (n *bitmapNode) without(shift int, hash uint64, key Key) node { 113 | return n 114 | } 115 | 116 | func (n *bitmapNode) find(shift int, hash uint64, key Key) (value interface{}, err error) { 117 | bitsToShift := uint(shift * fanoutLog2) 118 | pos := bitpos(hash, bitsToShift) 119 | if cMap := n.childBitmap; (pos & cMap) == 0 { //nothing in slot, not found 120 | err = errors.New(keyNotFound) 121 | } else { 122 | index := n.index(pos) 123 | if int(index) >= len(n.children) { 124 | err = errors.New("Keys computed index is larger than children") 125 | } else { 126 | value, err = n.children[index].find(shift+1, hash, key) 127 | } 128 | } 129 | 130 | return value, err 131 | } 132 | 133 | func (n *bitmapNode) pos() uint64 { 134 | return n.bitpos 135 | } 136 | 137 | //Shift key hash until leaf with matching key is found or key is not found 138 | func (t *PersistentMap) Get(key Key) (value interface{}, err error) { 139 | //Hash our key and look for it in the root 140 | hash := hash(key) 141 | value, err = t.root.find(0, hash, key) 142 | 143 | return value, err 144 | } 145 | 146 | func (t *PersistentMap) Insert(key Key, value interface{}) (n node) { 147 | hash := hash(key) 148 | _, n = t.root.assoc(0, hash, key, value) 149 | 150 | return n 151 | } 152 | 153 | func New() *PersistentMap { 154 | return &PersistentMap{ 155 | root: &bitmapNode{}, 156 | } 157 | } 158 | 159 | func StringKey(k string) Key { 160 | return Key([]byte(k)) 161 | } 162 | 163 | func IntKey(i int) Key { 164 | buf := new(bytes.Buffer) 165 | binary.Write(buf, binary.LittleEndian, i) 166 | return Key(buf.Bytes()) 167 | } 168 | 169 | func hash(a []byte) uint64 { 170 | h := fnv.New64() 171 | h.Write(a) 172 | 173 | return h.Sum64() 174 | } 175 | 176 | func shift(hash uint64, shift uint) uint64 { 177 | if shift == 0 { 178 | return hash 179 | } 180 | return hash >> shift 181 | } 182 | 183 | func mask(hash uint64, bshift uint) uint { 184 | return uint(shift(hash, bshift) & uint64(fanMask)) 185 | } 186 | 187 | func bitpos(hash uint64, bshift uint) uint64 { 188 | return 1 << mask(hash, bshift) 189 | } 190 | 191 | func (n *bitmapNode) index(onebitset uint64) uint { 192 | return popcount_2(n.childBitmap & (onebitset - 1)) 193 | } 194 | --------------------------------------------------------------------------------