Saturday, November 18, 2017

Corrections for Learning FP in Go Book

Errors and Corrections

While I did my best to present perfect information, we're all human and make mistakes.  The topics of category theory, functional programming and Go development are extensive and in my effort to organize and condense this information I likely made a few mistakes.  I will post corrections and any other information that might clarify issues in this book here.

If you find any errors and want to share your insights regarding this book feel free to add a comment to this page.  Thanks! Lex

UPDATE:  At the last minute, the Category Theory That Applies chapter 11 was moved to the end of the book.  So, it's best that you read it after immediately after chapter 8 as the author originally intended ;-)  It covers terms like Monoid, Functor, Monad, etc. used in chapters 9 and 10.

Get latest source code here.
If you find any errors, please post a comment on here.


Chapter 1

Words in chapter do not match code

The code is correct.  The words need editing.


Wording in book:




Only the final leaf nodes of are added together to calculate the sum total of 8:

Corrected wording:

Only the final leaf nodes of are added together to calculate the sum total of 5: 


Here's the code:

1-functional-fundamentals/ch01-pure-fp/02_fib/main.go


package main
import (
    "fibonacci")
func main() {
    println(fibonacci.FibSimple(4))
    println(fibonacci.FibMemoized(5))
    println(fibonacci.FibChanneled(6))
}





1-functional-fundamentals/ch01-pure-fp/02_fib/src/fibonacci/meomoize.go 


package fibonacci

type Memoized func(int) int
var fibMem = Memoize(fib)

func Memoize(mf Memoized) Memoized {
   cache := make(map[int]int)
   return func(key int) int {
      if val, found := cache[key]; found {
         return val
      }
      temp := mf(key)
      cache[key] = temp
      return temp
   }
}

func FibMemoized(n int) int {
   return fibMem(n)
}

func fib(x int) int {
   if x == 0 {
      return 0   } else if x <= 2 {
      return 1   } else {
      return fib(x-2) + fib(x-1)
   }
}

Output





URL Missing Dash

This is the correct url: https://github.com/golang/go/wiki/SettingGOPATH

URL for github Repo

The screenshots, links and text reference the repo (this repo) as l3x/fp-go.git, whereas it's actually l3x/learn-fp-go.git.

Error running first example

The screenshot associated to "go run cars.go", shows that one should go run cars.go in the following path: ~/myprojects/fp-go/1-functional-fundamentals/ch01-pure-fp/01_oop.

There is no cars.go in that path, it's actually in ~/myprojects/fp-go/1-functional-fundamentals/ch01-pure-fp/01_oop/src/oop. And even when you do run go run cars.go from that directory, you get:
go run: cannot run non-main package
The easy solution is to use the init script found in every project.  Here's how:
(Double-click image to enlarge)
For details see the How to build and run Go project section in the appendix. 

Many Thanks to Dave for reporting those issues here.

https://github.com/l3x/learn-fp-go/issues/5

If you're an MS Windows user, you should check this out.

Chapters 1 & 5


Any reference to KISS-Glide  should say Dot Init.  They mean the same thing.  It's the bash initialization script that's explained in detail in the How to build and run Go projects section of the appendix.

Chapter 2

Page 55 has typos
The map example on Page 55 has typos that makes the code unable to be ran with the expected results
  • planets used instead of names within collection.NewFromSlice()
  • strings.Join should be strings.Join([]string{"Hey", v.(string)}, " ") or strings.Join([]string{"Hey ", v.(string)}, "")
With these typos fixed
names := []interface{}{
  "Alice",
  "Bob",
  "Cindy",
}

collection := collections.NewFromSlice(names)
collection = collection.Map(func(v interface{}) interface{} {
  return strings.Join([]string{"Hey", v.(string)}, " ")
})
println(collection) // or the use of fmt.Println(collection)

See https://github.com/l3x/learn-fp-go/issues/6

Gleam - distributed MapReduce for Golang

The LuaJit+Go approach has been ditched because of complexity to add new features to Gleam. 

Gleam is now using pure Go everywhere.


Chapter 4

In  func (t *titlizeReader) Read(p []byte) (int, error) ...

There is a typo:  't' should be 'a'

In book:

        if p[i] >= 't' && p[i] <= 'z' {
         p[i] = p[i] - 32
        }
Should be:

        if p[i] >= 'a' && p[i] <= 'z' {
         p[i] = p[i] - 32
        }

 Chapter 9


The directions of the arrows are significant 

The g(x) equation should be:  g(x) = x 2 + 1

... rather than not  g(x) = x2 + 1

Chapter 11

Towards the end of the chapter in a section entitled, Fun with Sums, Products, Exponents and Types,  we talked about looking at structures algebraically and finding matching structures.  The image below should look familiar.  It's an updated, slightly improved version of what's in the book:


Appendix

Old content is found in chapter 4.  The new content was moved to the appendix.  It's a conversation that a Java developer a Go developer and an FP developer had regarding error handling in Go.

A Java, Go and FP developer Walk into a Bar...

DeveloperSays...
Java
I do not like the way Go makes me handle errors after every function call.
I want exception handling like this:
try {
   Decrypt()
   Authenticate()
   Charge()
}
catch (Exception e) {
   // handle exception
}
but after adding all those error checks my code looks more like this:
err := Decrypt()
if err != nil {
   return err
}
err = Authenticate()
if err != nil {
   return err
}
Charge()
Go error handling looks like a bunch of scaffolding  obscuring my code's intentions.
GoAs you know, Go does not support exception handling.  Instead, you return errors to inform the calling function/method that something went wrong.  You are supposed to handle an error as soon as possible.  This is what we call the idiomatic way to handle errors.  When you throw exceptions, how far up the call stack will your handler be?  What if your exception gets handled before it reaches your global error handler?  Will it stop or be rethrown?  If rethrown, do you really want to handle your errors more than once?
Go 
Also, since an error is a value we can program it and do.  See that Scan method?  It might encounter an IO error, but it does not immediately return the error.  Instead it returns a boolean value to indicate success.  Later we can check for the error.
scanner := bufio.NewScanner(input)
for scanner.Scan() {
   token := scanner.Text()
   // process token
}
if err := scanner.Err(); err != nil {
   // process the error
}
We can also clean up the following repetitive error checking code:
_, err = fd.Write(p0[a:b])
if err != nil {
   return err
}
_, err = fd.Write(p1[c:d])
if err != nil {
   return err
}
_, err = fd.Write(p2[e:f])
if err != nil {
   return err
}
// and so on
... by writing a helper that will allow us to wait until we're done writing to handle the error. 
var err error
write := func(buf []byte) {
    if err != nil {
        return
    }
    _, err = w.Write(buf)
}
write(p0[a:b])
write(p1[c:d])
write(p2[e:f])
// and so on
if err != nil {
    return err
}

JavaThat is much better, but is that a reusable pattern?  In each of those two examples above, we had to write custom error handling code in two different places, right?  Isn't there a more generic, yet Go idiomatic way to handle errors that will eliminate that repetitive error checking code and allow me to check for errors in one place, at the end of my workflow?
FP
Yes, Java developer there is and it's called the Lexical Workflow Solution.  Read all about it in chapter 11.  If you use it, your code will look something like this:
step = Next(step, Decrypt)
step = Next(step, Authenticate)
step = Next(step, Charge)
json, err := step(nil)
if err != nil {
   // handle error
}
The Go developer's error handling code samples above came from https://blog.golang.org/errors-are-values



1 comment:

  1. package fibonacci

    type Memoized func(int) int
    var fibMem = Memoize(fib)

    func Memoize(mf Memoized) Memoized {
    cache := make(map[int]int)
    return func(key int) int {
    if val, found := cache[key]; found {
    return val
    }
    temp := mf(key)
    cache[key] = temp
    return temp
    }
    }

    func FibMemoized(n int) int {
    return fibMem(n)
    }

    func fib(x int) int {
    if x == 0 {
    return 0 } else if x <= 2 {
    return 1 } else {
    return fib(x-2) + fib(x-1)
    }
    }

    This code does not memoize functions.
    fib function is executed in a row.
    Sorry my English.

    ReplyDelete