Adventure in Go

From A Node.js Developer

This is Me

Soares Chen

@soareschen

JavaScript

3 years experience 

Quiver.js

  • Next-Generation web framework
  • ES6
  • Declarative components
  • Functional programming in steroid using object-oriented programming

http://quiverjs.org

Go

2 months experience

C/C++

6+ years of experience

Many More Languages

Over last 12 years

Design of Programming Languages

I care about

over

hypes, horror or success stories

What You Can Expect From Me

  • Objective but opinionated comparison on Go
  • Good & bad parts
  • Best practices from other languages

Tell Me I am Wrong

I am here to learn, just as you are

Go is a

weird language

First Impression

Go is a static typed language

Go is a dynamic typed language with static typed compilable primitives

Good part: Struct

  • Similar to C struct
  • No inheritance hell
type Character struct {
  Name string
  HP, MP, Level int
}

Bad Part: Struct Initialization

  • Anyone can initialize new struct
  • Ugly "&" character for pointer initialize
var swordsman *Character

swordsman = &Character{
  Name: "dark_knight",
  HP: 100,
  MP: 30,
  Level: 4,
}

Bad Part: Nullable Defaults

  • All fields are optional
  • Pointer fields can be null
swordsman := &Character{
  HP: 100,
  Level: 4,
}

swordsman.Name // ""
swordsman.MP // 0

So Go does the static type checking for you..

But you have to do all null-checking yourself?

The Problem

Go uses structs for two kind of purposes

  • Records
  • State Management
type Person struct {
  Name string
  Age int
  Company *Company
}

Best Practice

Records as plain structs

  • Plain structs used as records
  • No method - never upgrade to interface
  • Interface implies encapsulation, which public structs lack
type Person struct { ... }

func validatePerson(person *Person) error { ... }
func estimateSalary(person *Person) int { ... }

person := &Person{...}
err := validatePerson(person)
salary := estimateSalary(person)

Best Practice

Use constructor function

  • Never create struct manually
  • Never modify fields directly
var character, monster *Character

swordsman := createCharacter("dark_knight", ...)
swordsman.receiveExperience(15)

Ideally

Use Interface

  • Never create struct manually
  • Enforce attribute constraints
  • getter/setter, etc
struct character { ... }

interface Character {
  GetHP() int
  GetMP() int
  GetLevel() int

  Attack(enemy Character, damage int)
  ReceiveExperience(exp int)
}

Go: the bad part

No getter/setter Interface Methods

  • Can't use interface like a struct with transparent getter
  • Hurts performance
struct character { ... }

// Note: Not possible
interface Character {
  get Name() string
  get HP() int
  get MP() int
  ...
}

swordsman = createCharacter("dark_knight", ...)
swordsman.Name // "dark_knight"

Go: the bad part

Cannot define methods on interfaces

struct character { ... }

interface Character {
  Name() string
  HP() int
  MP() int
}

interface Attacker {
  Attack(enemy Character, damage int)
}

// Note: Not possible
func (c Character) Attack(enemy Character, damage int) {
  ...
}

Go: the good part

Interface Upgrade

  • Can optimize code if object can upgrade to more specialized interface
func saveTheWorld(character Character) {
  superman, ok := character.(Superman)
  if ok {
    ...
  }
}

Go: the bad part

Cannot add new upgrade to Interface

interface Character { ... }
interface Superhero { ... }
interface Villian { ... }

func giveSuperpower(character Character) Character {
  return &characterWithSuperpower{character, ...}
}

func destroyWorld(character Character) {
  villian, ok := character.(Villian)
  if ok { ... }
}

joker := createVillian(...)
superJoker := giveSuperpower(joker)
destroyWorld(superJoker) // Is superJoker still a Villian?

JavaScript ES6

  • Duck-typing
  • Prototype inheritence
  • getter/setter
  • Symbol fields
  • Proxy

Yes if you think Go interface upgrade is cool

Does it matter?

Other Go vs Node

Functional Programmnig

func createCounter() func() int {
  counter := 0

  return func() int {
    return counter++
  }
}
var createCounter = function() {
  var counter = 0

  return function() {
    return counter++
  }
}

Error Handling

  • I want one liner in Go
  • if(err) return err
func manageUser() error {
  db, err := connectDatabase(...)
  if err != nil {
    return err
  }

  user, err := getUser(db, ...)
  if err != nil {
    return err
  }

  ...
}
var manageUser = function(callback) {
  connectDatabase(..., function(err, db) {
    if(err) return callback(err)

    getUser(db, ..., function(err, user) {
      if(err) return callback(err)

      ...
    }
  }
}

Http Handler

  • Same middleware architecture
func (ctx *MyHandler) ServeHTTP(
  w http.ResponseWriter, req *http.Request, next http.HandlerFunc) 
{
  ...
}
var myHandler = function(request, response, next) {
  ...
}

Package Naming

  • Go, you have copied it wrongly
  • Naming hell
import (
  user "myapp/user"
  post "myapp/post"
}

u := &user.User{...}

p := &post.Post{
  User: u,
  ...
}
var user = require('myapp/user')
var post = require('myapp/post')

var u = new user.User(...)

var p = new post.Post({
  user: u,
  ...
})

Naming Workaround

  • Use "Lib" to postfix packages
import (
  userLib "myapp/user"
}

user := &userLib.User{...}
var userLib = require('myapp/user')

var user = new userLib.User(...)

ES6 solves the pain shared in both Go and Node

  • Promises
  • Modules

ES5 < Go < ES6

Go or JavaScript?

Node

io.js

Still, use the right tool for the right job

  • ES6 still in development, Go is stable and mature
  • Go HTTP libraries are as good as Node Express, if not better
  • Many advantages/disadvantages as a compiled language

If only JavaScript programmers can write their code more like Go

And Go programmers can write their code more like JavaScript


The world would be a better place

Adventure in Go

By Soares Chen

Adventure in Go

  • 2,091