Alex

More on Interfaces (Video 20)

var r io.Reader // nil interface here
var b *bytes.Buffer // nil value here

r = b // at this point r is no longer nil itself, but it has a nil pointer to a buffer

The Error Interface

type error interface {
  func Error() string
}
type someErr struct {
  err error
  someField string
}

func (e someErr) Error() string {
  return "this is some error"
}

func someFunc(a int) *someErr { // We should NEVER return a concrete error type
  return nil
}

func main() {
  var err error = someFunc(123456)

  if err != nil {
    // Even though we logically didn't want to throw an error, returning a concrete error type 
    // meant that the err variable was initialised and looks like (*someErr, nil) which in the
    // semantics of interfaces ISN'T NIL
    fmt.Println("Oops")
  } else {
    // If we'd done err := someFunc(123456), the above check would have worked although again we 
    // should never return a concrete error implementation from a function
  }
}

More on Pointer vs Value Receivers from Matt Holiday Vid

Interfaces in Practice

  1. Let consumers define the interfaces; what minimal set of behaviours do they require
    • This lets the caller have maximum freedom to pass in whatever it wants so long as the interface contract is adhered to
  2. Reuse standard interfaces wherever possible
  3. Keep interface declarations as small as possible - bigger interfaces have weaker abstractions
    • The Unix file API is simple for a reason
  4. Compose one method interfaces into larger interfaces (if needed)
  5. Avoid coupling interfaces to particular types or implementations; interfaces should define abstract behaviour
  6. Accept interfaces but return concrete types

Be liberal in what you accept, be conservative in what you return

Empty Interfaces

Revisiting Understanding nil

Zero Values

Nil

Understanding the Different Types of nil

Pointers

Slices

Maps, Channels and Functions

Nil Interfaces

func bad1() error {
  var err *someConcreteError
  return err // We are returning (*someConcreteError, nil) which !=nil
}

func bad2() *someConcreteError {
  // We are returning a concrete pointer to an error which will pass ==nil, however
  // it is very bad practice because the second you wrap this pointer in the error
  // interface you will have the same problem as above
  return nil
}

How is Nil Useful

Nil Pointers

Nil Slices

Nil Slices

Nil channels example

Function Currying

func Add(a, b int) {
  return a+b
}

func main() {
  var addTo5 func(int) int = func (a int) int {
    return Add(5, a)
  }
}

Method Values

func (p Point) Distance(q Point) float64 {
  return math.Hypot(q.X-p.X, q.Y-p.Y)
}

func main() {
  p := Point{1,2}
  q := Point{4,6}

  distanceFromP := p.Distance // Here we close over the receiver value p, returning a curried function
}

Notes From Homework #4

type dollars float32

func (d dollars) String() string {
    return fmt.Sprintf("$%.2f", d)
}

type database map[string]dollars

func (db database) list(w http.ResponseWriter, req *http.Request) {
    for item, price := range db {
        fmt.Fprintf(w, "%s: %s\n", item, price)
    }
}

func main() {
    db := database{
        "shoes": 50,
        "socks": 5,
    }
    http.HandleFunc("/list", db.list)
    log.Fatal(http.ListenAndServe("localhost:8080", nil))
}