Phase 5 – Porting packages

Phase 4 and Phase 5 need to be done one after the other for each package.

Each package we've defined must now be ported from the original source to Go. This work can be distributed efficiently among multiple developers, with each team typically working off its own branch.

There might be a few decisions to make here, especially if the source includes things such as generics. We should list down the principles before starting coding. For example, for generics, the options for porting include the following:

  • Check whether generic classes are really used or can be flattened out. Often, the generic code is just so that the earlier developer could try out a cool, new trick they had learned from a blog, rather than a real requirement in the code.
  • Explode the generics into specific implementations. If the generics class is used for a few specific purposes, just bite the bullet and create two implementations. A real-life example can be found in the strings and bytes packages of the standard library: both have very similar APIs but different implementations.
  • Engineer the generic nature using an interface. This involves the following:
    • Listing the set of operations that the generic algorithm or container needs
    • Defining an interface with these methods
    • Implementing the interface for each instantiation of the generic in the source
  • Use interface{}, this allows us to store generic types in a reference. If the original generic does not care about the actual type of the data being stored, using an interface with a type assertion at the client can lead to generic Go code (https://github.com/cookingkode/worktree/blob/master/worktree.go ). The downside of this technique is that we lose the compile-time type-checking and increase the risk of runtime type-related failures.
  • Use a code generator, there are many tools, such as genny (https://github.com/cheekybits/genny), that take a template and then generate type-specific code. For example, the following code defines a template for a generic list type:
package list   
   
import "github.com/cheekybits/genny/generic"   
   
//go:generate genny -in=template.go -out=list-unit.go   gen "Element=uint"   
   
type Element generic.Type   

type ElementList struct { list []Element } func NewElementList() *ElementList { return &ElementList{list: []Element{}} } func (l *ElementList) Add(v Element) { l.list = append(l.list, v) } func (l *ElementList) Get() Element { r := l.list[0] l.list = l.list[1:] return r }

Here, Element is an identifier for the generic type. The Go generate comment causes Go generate to generate unit-specific code in a separate file, called list-unit.go. The generated code looks like this:

// This file was automatically generated   by genny.   
// Any changes will be lost if this file   is regenerated.   
// see   https://github.com/cheekybits/genny   
   
package list   
   
type UintList struct {   
            list []uint   
}   
   
func NewUintList() *UintList {   
            return &UintList{list:   []uint{}}   
}   
   
func (l *UintList) Add(v uint) {   
            l.list = append(l.list, v)   
}   
   
func (l *UintList) Get() uint {   
            r := l.list[0]   
            l.list = l.list[1:]   
            return r   
}   

The client can then use this UintList through a normal import. As seen, code generators such as this allow you to write effectively generic code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset