@h12.io

Go vs Python

27 September 2019

Slices

Go slice and Python slice have very similar syntax, but Python slice is a shallow copy of part of the original list, while Go slice is just a new range within the same underlying array of the original slice.

Let’s try:

a = [1, 2, 3]
b = a[:2]
b[0] = 9
print(a)
print(b)

# output:
# [1, 2, 3]
# [9, 2]

See a[0] remains the same.

package main

import (
    "fmt"
)

func main() {
	a := []int{1, 2, 3}
	b := a[:2]
	b[0] = 9
	fmt.Println(a)
	fmt.Println(b)

	# output:
	# [9 2 3]
	# [9 2]
}

See a[0] changes because slice a and b shares the same underlying array.

Learning Frontend

15 February 2019

JavaScript Surprises to a Go Developer

12 February 2019

The scope of var is wrong

Never use var to declare variables, use let instead.

REF

== is conversion and comparison

What you really need is ===, which is similar to comparing two interface{}.

=== is shallow

{a:1}==={a:1} is false, while Go struct with string are compared by contents (but not for slice).

Also this affects map key comparison. So object key is not so useful in JS as struct key in Go. The equality test is always on the references rather than the contents.

REF

const works for objects, but the contents are modifiable

REF

Single and double quotes are the same

REF

Back quotes are like text/template

Escapes also works in back quotes and more template related features.

REF

Switch cases fall through by default

Access undeclared object property returns undefined

Trying to access undeclared variables throws an exception but accessing undeclared object properties return a value called undefined.

Use 'property' in obj to check if a property is declared or not.

Only float64 is supported

Object is like map[string]interface{}

Array is like map[int]interface{}

Map is like map[interface{}]interface{}

for...in traverses properties recursively

for...of is like for...range in Go

object.constructor.prototype

    console.log({}.constructor === {}['__proto__'].constructor);
    console.log({}.constructor.prototype === {}['__proto__']);

Promise can be chained and composed

It is quite impressive how JS do concurrency differently and efficiently.

Promise.then(onFulfilled) where onFufilled can return another Promise.

Promise.all([func1(), func2(), func3()])
.then(([result1, result2, result3]) => { /* use result1, result2 and result3 */ });

Computed property name

{
    [prop]: 42,
}

There are two types of generator method syntax

{
    *gen() {

    }
}
{
    gen: function*() {

    }
}

Run: Graceful Goroutine Orchestration

14 December 2018

GoDoc

Overview

While Go provides goroutines, channels and selects as first-class citizens to support concurrent programming, it is not trivial to combine these elements to address important concerns of goroutine orchestration, e.g. error handling, panic recovery, goroutine leak prevention, goroutine reuse, goroutine throttle and logging.

The package provides a mini-framework to address those cross-cutting concerns.

Quick start

go get -u h12.io/run

Here is an example illustrating the usage of the goroutine pool and the group. The task is described in the “Google Search 2.0” page from this slide.

// the goroutine pool
pool := run.NewGoroutinePool(
	run.Max(8),                // the pool contains maximum 8 goroutines
	run.IdleTime(time.Minute), // a goroutine will stay in idle for maximum 1 minute before exiting
)

// the group
// the goroutine pool might have longer lifespan than the group
group := run.NewGroup(
	context.Background(), // a context that can cancel the whole group
	run.Pool(pool),       // the goroutine pool used by the group
	run.Recover(true),    // recover from panic and returns the PanicError
	run.Log(func(info *run.LogInfo) { // a log function for all starts/stops
		log.Print(info)
	}),
)

searches := []*GoogleSearch{
	{Search: Web, Query: "golang"},
	{Search: Image, Query: "golang"},
	{Search: Video, Query: "golang"},
}
for _, search := range searches {
	// start searching in parallel
	if err := group.Go(search); err != nil {
		log.Fatal(err)
	}
}

// wait for all searches stop
if err := group.Wait(); err != nil {
	log.Fatal(err)
}

for _, search := range searches {
	fmt.Println(search.Result)
}

See the full example here.

Design

The package is built around the concept of a runner.

type Runner interface {
	Run(context.Context) error
}

A correct implementation of a runner should satisify the following conditions:

  • blocks when the work is on going
  • returns when all work is done, an error occurred or context is cancelled

With goroutine pool and group in the package, the user does not need to use the go statement explicitly, but only needs to implement their objects satisfying the Runner interface.

A Group is useful when multiple concurrent sub-tasks needed to be combined as a single task (the task failed when one of them failed, every sub-task should be cancelled when the task is cancelled).

A Pool is useful when there are many short-lived gorotuines.

Group can be built upon pool, not vice versa.

How to Start Godoc on Mac

5 May 2018

Fish:

godoc -http=:6060 &; disown (pidof godoc)

Bash:

godoc -http=:6060 &; disown `pidof godoc`

And pidof can be installed with Homebrew.

Tmux Cheatsheet on Mac

20 April 2018

Create a new session with iTerm2 integration

tmux -CC new -s [session-name]

Attach to a session with iTerm2 integration

tmux -CC attach -t [session-name]

How to Diff two JSON Files

10 April 2018

Just sort the keys first!

Example:

cat a.json | jq --sort-keys . > aa.json
cat b.json | jq --sort-keys . > bb.json
vimdiff aa.json bb.json