Saturday 5 October 2019

Overview

I have reorganized this Overview since all my posts in the past few years have been about Go which is now near the top.  My previous pet subject (Agile) is now towards the bottom.

Coding

Go Coding

The Essence of Go - reading about Go you don't realize how nice it is to use
Improving Go Error Handling - how to fix one part of Go that is not so nice
Active Object - a powerful concurrency pattern that Go makes easy
Type Safe Channels - avoiding bugs when a channel must accept multiple types
Go Puzzles - 33 coding puzzles to test your knowledge of Go
Puzzle Solutions - don't look at the answers till you've tried the puzzles
Go Error Handling Using Closures - response to Error Handling Draft Design
Example Types as Contracts - response to 2019 Generics Draft Design

C Coding

Best Practice in C for Modules - high-coupling and other things to avoid
Defensive Programming - how it works and how it can hide bugs
Shotgun Initialization - a defensive programming practice to avoid
Alignment and #pragma pack - make structs "alignment agnostic" to avoid surprises
Making Code Testable - coding for testability improves correctness, reliability, etc
Ten Fallacies of Good C Code [CodeProject] - 10 more things to avoid

C++ Coding

STL's Dark Secret - vectors are slower than they should be
Iterators Through the Looking Glass - subtleties of the STL reverse iterators
C++11 and Lambda Functions - lambda functions make STL so much better
Nested Functions using Lambdas - you can finally have nested functions in C++11

C# Coding

Why I Like C# - lots of nice things (and it's not really that much slower than C)
Overflow Checking using checked/unchecked [CP] - C# has some cool features
Nested Functions using Lambdas - includes an example of using C# lambdas

Maintainability

Long Identifiers make Code Unreadable - don't try to put too much info. into a name
Self Describing Code - why it's a bad idea and why you should comment your code

Other

Zero - bugs are less likely if you don't treat zero as a special case
Asymmetric Bounds - in code and GUI design this is an important way to avoid bugs
Book Review: Clean Code - a great book on creating the best code
The Phillips Scale of Code Quality - how good is your code?
Version Control - Personal Experiences - hands on version control

Software Design

Design Principles

Handling Software Design Complexity - what software design all boils down to
DIRE - an obvious thing we often forget
Developer Quality Attributes - or why fixing bugs is not important
Verifiability - software is useless unless you can verify its correctness
Why Good Programs Go Bad - risk avoidance causes software to "rust"
Book Review: 97 Things Every Architect Should Know

Design Practices

Fundamentals of Software Design - 8 ways to create a good design
Agile Design - how emergent design almost always works better than BDUF
Inversion of Control - IOC is a technique for better decoupling using DIRE
Dependency Injection - an example of IOC

Anti-Patterns

Gas Factory Anti-Pattern - a mistake even (or especially) good designers make
Reusability Futility - "Simplicity before Generality, Use before Reuse"
Shotgun Initialization - an example of the dangers of defensive programming
Layer Anti-Pattern - the problems of a common, obvious approach
Ignore Divide By Zero - commonly taught practices can be wrong
Defensive Programming - it can hide bugs

Agile

Principles

Agile's Fifth Element - favor simple design over re-usability and generality
JIT (Just In Time) - an example of DIRE that is core to much of Agile
DIRE (Don't Isolate Related Entities) - how you divide and conquer is the key
Agile Design - evolving software one small step at a time
Agile and Code Reuse - all about YAGNI (you ain't gonna need it)
Software Quality Assurance & Agile - how Agile evolved from, but is different to, SQA
Lean is not Agile - applying "eliminate waste" to software design leads to BDUF
Software Development Methodologies [CP] - Agile and other methodologies by analogy
Scrum is like Communism? - maybe it is, but that does not mean it is doomed to failure!

Team

Agile Roles - when Agile is done properly all roles do change (some even disappear)
Scrum Team Size - teams should be small to avoid social loafing and other phenomena
Scrum Team Composition - "feature" teams are the key
Collaboration - traditional development discourages collaboration + why Scrum works
Production Line Mentality - the reason teams don't collaborate

Making Agile Work

Scrum Standup - it's more about visibility than communication
Developer Quality Attributes - what benefits developers eventually helps users
Agile Version Control - Agile requires the right version control practices & software (Git)
Scrum Problems - management "buy-in" & other things that help Scrum work properly
Why Scrum Fails - intransigence, non-collaboration, etc
Written vs Verbal - when, who, why, and how of Agile documentation
JIT Testing - testing as you go (continuous testing) is an example of JIT (Just In Time)
Vendor Mentality - another "mentality" you need to overcome
Customer Management - the customer is not always right

Unit Tests

Change - how Unit Tests help you to embrace change
What's so great about Unit Tests - Unit Tests are not about finding bugs
White Box Testing - the best Unit Tests use "good" white box testing
Personal Experiences with Unit Testing - it took me 20 years to truly appreciate them
Challenges - why getting started with Unit Tests seems, but is not, insurmountable
Unit Tests Best Practice - a few things to avoid
Arguments Against Unit Tests - common arguments and why most are invalid
Summary - Unit Tests concisely summarized



Saturday 24 August 2019

Go: Example Types as Contracts

Summary 

The sticking point with adding parametric polymorphism to Go is how to specify which type(s) are allowed to be used as type parameters. Contracts are a good way.  A simpler way is to use example types as detailed below. I believe this would make creating generic functions/datatypes simpler and easier to fathom but with the same benefits as contracts. 

Introduction

I think the 2019 Contracts Draft Design is great. I would be more than happy if it were implemented, but there are some who think the use of contracts could be simplified or avoided and maybe they are right. 

[Personally, I have been using C++ templates for more than 20 years and am happy with having no way to specify constraints on type parameters. (That is C++ (before Concepts) leaves it to the compiler to at some point detect if a specific type does not provide a capability required by a type parameter.) But I concede that even the best C++ compilers produce tedious (but ultimately decipherable) error messages and this is just not acceptable for Go where getting code to build is much much easier than C++ and other languages.  Moreover, as pointed out in the Draft Design, a mechanism such as contracts can make code more resilient by preventing a change to a generic function/type having an unintended change to the type parameter requirements and consequently breaking distant code.]

BTW From now on I will use the term generic as a shorthand for parametric polymorphism.

Go is renowned for making things simple, so my approach was to look at how I (and others) use "generics" in other languages then look at how to make that process as simple as possible. In my experience, generic functions and datatypes are generally created in a vanilla (non-generic) version first, then converted.  In other words rather than starting with a type parameter off the bat, you start with a simple "example" type first, get it working, then convert the example type to a type parameter.

Using Example Types

My proposal is to stick with the example type in Go and just indicate that it can be replaced with other types, as long as they have the same capabilities.  Of course, it's not that simple, but I will address the issues shortly. 

First, a simple example will help.  Let's start with the most obvious generics example – a function to find the minimum value.  Here is the Go code using int. 

func Min(first int, rest ...int) (retval int) {
  retval = first
  for _, v := range rest {
    if v < retval {
      retval = v
    }
  }
  return
}
  
To convert we make one small change to indicate that int is a type parameter.  Thence anywhere int is used it is replaced with the type of the type parameter. 

func Min(type int)(first int, rest ...int) (retval int) {
  retval = first
  for _, v := range rest {
    if v < retval {
      retval = v
    }
  }
  return 
}
  
Now you can use Min() with any integer type like rune or uint64. 
  
The first problem you will probably have already seen is that you may need to use an actual int in the code and not have it take on the generic type.  To do this (and make the code more resilient to changes) you can define a new type. 

type minT int
func Min(type minT)(first minT, rest ...minT) (retval minT) {
  retval = first
  for _, v := range rest {
    if v < retval {
      retval = v
    }
  }
  return 
}

Note that by convention you might be inclined to use a name for the type of T - but something like minT is safer as it is not exported and is less likely to conflict with other uses of generics in the package. 

New Orderable Type 

Now the only reason we use int as the "example type" in the generic Min() function is so we can test the order of values, using the less than operator (<) in this case.  But strings and floating point types also support less than.  We should be able to get a minimum value for those too.  That is why I propose adding a new (non-instantiable) type to the language called orderable.  Now Min() can work with strings too. 
  
func Min(type orderable)(first orderable, rest ...orderable) (retval orderable) {
  retval = first
  for _, v := range rest {
    if v < retval {
      retval = v
    }
  }
  return 
}

Note that you can't declare a variable of type orderable except in a generic function or type that uses an orderable type parameter. 

The compiler knows what types support ordering so this approach will adapt (unlike the 2019 Contracts Draft Desing I believe) to future language changes - for example if an int128 type is added or if ordering (<, >=, etc) is later extended to other types such as some subset of structs. 

We might also want to hide the public name of this new type in the generics package, to avoid polluting the global namespace and possibly introducing conflicts with existing code. In other words the public type would be generics.Orderable to hide the actual type (possibly with a name like __orderable). 

Required Methods 

Another important requirement for a type parameter is that it implements particular method(s).  Here we demonstrate the common example of a function to convert a slice to a slice of strings.  We need to ensure that the function is only called using a slice of a type that implements the String() method. 

type stringer struct {} 
func (stringer) String() string { return "" } 

func Stringify(type stringer)(s []stringer) (retval []string) { 
    for _, v := range s { 
        retval = append(retval, v.String()) 
    }
    return 
}
  
Here stringer is the "example type" for the generic Stringify() function.  One ugliness is that we have to implement a dummy String() method on the stringer type, but it doesn't really do much and the compiler would not need to generate any code for this function since it is never called.

New Any Type 

The above stringer type is based on an empty struct on the assumption that this can represent any type in Go.  However, now that I think about it, this may preclude using Stringify() with a slice of some types.  Maybe we also need to add a type to the language that can stand in for any type. 

type stringer generics.Any 
func (stringer) String() (retval string) { }

New Comparable Type 

Comparing types for equality is a little complicated in Go.  Even the 2019 Contracts Draft Design proposes a special predefined contract called "comparable" (which, I assume, is intended to be handled specially by the compiler).  Similarly, in my proposal we have a new Go type called generics.Comparable that includes all types that can be compared. (A cursory explanation is that slice, map, function types are non-comparable plus any array/struct/interface types that contain such non-comparable type(s).) 

Here is an example to demonstrate using the example from the 2019 Contracts Draft Design:

type indexT generics.Comparable 

func Index(type indexT)(s []indexT, x indexT) int { 
    for i, v := range s { 
        if v == x { 
            return i 
        } 
    } 
    return -1 
}

Types using Type Parameters 

I have unintentionally concentrated on generic functions above.  Just for completeness I present a generic type as well – the ubiquitous pair.


type pairT1 generics.Any 
type pairT2 generics.Any 

type pair(type pairT1, pairT2) struct { 
    first  pairT1
    second pairT2
}

This also demonstrates how to have more than one type parameter using the same example type. That is, T1 and T2 use the same example type but can be instantiated with different actual types.

Mutually Referencing Type Parameters 
  
Of course, a function or type can have multiple type parameters, which can refer to each other. Again using an example from the 2019 Draft Design.

type node generics.Any
func (node) Edges() (edges []edge) { return } 

type edge generics.Any 
func (edge) Nodes() (from, to node) { return } 

type Graph(type node, edge) struct { ... } 
func New(type node, edge)(nodes []node) *Graph(node, edge) { ... } 
func (g *Graph(node, edge)) ShortestPath(from, to node) []edge { ... }   

Further Constraints 

In my proposal certain types when used as a type parameter can stand in for any type that has the same capabilities. So int can stand in for all integer types. However, we may need further restrictions on allowed types for a type parameter such as only integers of at least 16 bits.  In this case perhaps int16 could mean any integer of 16 bits or more and uint could mean any unsigned integer, etc.

If this does not work well perhaps allow a new generic type to be defined from a set of existing types.  This is similar to specifying a list of types in a contract in the 2019 Contracts Draft Design.

package generics

type UInt16 {
uint16, uint32, uint64}  // maybe uint128 later
type SInt16 
{int16, int32, int64}
type Int16 {int16, int32, int64, uint16, uint32, uint64}
type Int16 {UInt16, SInt16}

Embedding

As in the 2019 Draft Design example types can "embed" other types using the existing type embedding mechanism.

type printStringer struct{stringer} // embed stringer type from above
func (printStringer) Print() {}
  
Alternatively you can just extend the existing type by adding a method.

type printStringer stringer
func (printStringer) Print() {}

Conclusion

I actually wrote a draft of this proposal late last year after hearing Rob Pike's talk at the Sydney Go Meetup (October 2018?) about the 2018 Draft Designs for Go 2.  Unfortunately, I never found the time to complete it, and was hoping that someone else would put forward a similar proposal.  (The most similar proposal I have seen is Matt Sherman's [
https://clipperhouse.com/go-generics-typeclasses/].) 

Now that I have reviewed and polished this proposal I think it's rather similar to the 2019 Contracts Draft Design but replaces the contracts with something more familiar and easier to grasp. The new contracts syntax is an improvement on the 2018 Contracts Draft Design but is still confusing - a contract with methods looks much like an interface but must also include the receiver type for each method which looks out of place.

This proposal makes it easy to write functions and datatypes in a non-generic version and convert them to generic ones. Please post a comment below if I have missed something.