go language generics system

Related tags

Build Tools gotgo
Overview

Gotgo

This document describes the third iteration of my attempt at a reasonable implementation of generics for go based on the idea of template packages.

Quick start

You can compile gotgo by typing

./build

and you can run it on an example template file by typing

./gotgo/gotgo pkg/gotgo/slice.got int string

A complete example is in the example/ subdirectory.

cd example
./build
./example

If you want to see what a template package will look like, check out slice.got, which is a simple package exporting handy functions for slices, such as Map, Fold, Filter, Append, Cat (concat).

The got file

A template package is contained in a got file, ending with the .got extension. As you might be able to guess, "got" stands for "go template". If you call gotgo with a --package-name flag, you can generate a go file which is a member of any package you like.

The got file differs from an ordinary go file only in its package line, which will be something like

package slice(type a, type b a)

This defines a package template named "slice", with two type parameters. The first could be any type, which will be called "a", and the second type, called "b" will default to be the same as "a", if no second type parameter is provided. The default type argument is optional, and will itself default to interface{}. The default type should be either an interface type, or a type defined in terms of other template parameters, although I'm not sure whether/how to enforce this. Your got file should be able to compile with the default type!

Within this package, the types "a" and "b" will be in scope, so the slice got file might contain:

func Append(slice []a, datum a) []a {
    // first expand if needed...
    slice = slice[0:len(slice)+1]
    slice[len(slice)-1] = datum
    return slice
}

We could also have a function that uses both "a" and "b":

func Map(f func(a) b, slice []a) []b {
    out := make([]b, len(slice))
    for i,v := range slice {
        out[i] = f(v)
    }
    return out
}

Importing a got package

If you want to use the above package, you typically do so by creating an ordinary go file that has a special import statement that specifies the types desired. Currently, this is not automated, as gotgo is in rapid flux.

import intslice "./slice(int)"

func foo(x int, xs []int, ...) {
    ...
    intslice.Append(xs,x)

This import is valid go (since the language spec states that the interpretation of the import string is implementation-dependent), and is accepted by the go compiler, so long as the slice(int) package has been generated and compiled.

The package name of this templated import may be dependent on the gotgo implementation unless you use the --package-name flag when running gotgo.

Getting fancier with interface types

We can get a bit fancier in the package specification by using interfaces.

package list(type a fmt.Stringer)

import fmt "fmt"

type T struct {
    Datum a
    Next *T
}
func (l T) String() String {
    if l.Next != nil {
        return l.Datum.String() + ", " + l.Next.String()
    } else {
        return l.Datum.String()
    }
}

This isn't much different from what I've already described, but there is (will be) an additional twist that isn't shown, which is that if you make the default type an interface, then any type parameter passed to the template must satisfy that interface! This is valuable because it means that you can guarantee that you can't break the compile of any client code unless you change the template signature.

If you write a template that requires a numeric type, such as

package maxmin(type a int)

func max(x,y a) a {
    if x > y { return x }
    return y
}

then you'll need to use a numeric type as your default. The gotgo system will then enforce that this package may only be parametrized with types that are assignment-compatible3 with int (or whatever default type you choose).

How does it work?

There is a simple program called gotgo, which you pass the filepath to a .got file and a list of types. By default, gotgo outputs the go code to stdout, but if you specify -o foo.go, it will create a file named foo.go.

A zeroconf go build system will need to track down and build imported packages, and in order to work with gotgo, will need to know how to build templated packages. An sketch of a helper for such a build system is provided in the gotimports program, which (non-recursively and incorrectly) dumps some make rules that might be helpful in creating your own Makefile.

Problems solved by gotgo

  • In go, there is no way to write a generic data structure, such as a linked list. As the FAQ points out, this isn't such a problem, since go has a pretty nice stable of built-in data structures, but it is a problem, since sooner or later you'll run into a need for more complicated structures (e.g. perhaps a red-black tree), and you don't want to reimplement them for every data type you use.

  • In go, there is no way to write a generic function whose return type depends on its input, or with two arguments of the same (but variable type). This is a much more serious shortcoming than the previous one, as it means you can't even write generic functions for the existing built-in generic data structures. For example, consider the generic version of

    func Append(x int, xs []int) []int
    

    which will append the given value to the slice, expanding it if needed. This is precisely the sort of function you'd like to write only once (or twice perhaps, to treat "large" data types differently), but currently copy-and-paste is the only way to reuse this code. (And similarly for methods...)

  • Compile time penalty of templates. In C++, using templates means recompiling the template every time you use it. Using gotgo, however, templates are separately compiled, just like any other go package. So a template need be compiled once by gotgo, and then once for each type it is parametrized by, which is the minimal number of recompiles consistent with allowing the compiler to generate optimal code for each type.

  • In the latest gotgo, you can use the --package-name flag to generate a go file in a given package. This makes it possible to use generics with private data types, or in the same package that defines a data type.

You might also like...
Experiments with Go generics

generics Quick experiments with Go generics algebra, a generic square root function for float, complex and and rational. future, a concurrent cache ("

Aboriginal Generics: the future is here!
Aboriginal Generics: the future is here!

Aboriginal Generics: the future is here! Inspired by this gem of an idea (click the image to go to the original comment): Installation go get github.c

Example code for Go generics

go-generics-example Example code for Go generics. Usage $ go build -gcflags=-G=3 Requirements Go 1.17 or later Advertise Go 言語にやってくる Generics は我々に何をも

Interactive Go interpreter and debugger with REPL, Eval, generics and Lisp-like macros

gomacro - interactive Go interpreter and debugger with generics and macros gomacro is an almost complete Go interpreter, implemented in pure Go. It of

An implementation of standard generics APIs in Go.

generics This package shows an implementation outlook of proposed generics APIs import "changkun.de/x/generics" Related issues: golang/go#45458 golang

Collection of unusual generics usecases in Go

Unusual Generics Type parameters or Generics in Go designed to reduce boilerplate for container data types like lists, graphs, etc. and functions like

It's a typed HTTP server in Go, using generics that is expected to come out with 1.18

typed-http-server-in-go This is a prototype of a typed HTTP server in Go via generics. Generics are scheduled to come out with Go 1.18, and you can pl

Package truthy provides truthy condition testing with Go generics
Package truthy provides truthy condition testing with Go generics

Truthy Truthy is a package which uses generics (Go 1.18+) to create useful boolean tests and helper functions. Examples // truthy.Value returns the tr

Go 1.18 Generics based slice package

The missing slice package A Go-generics (Go 1.18) based functional library with no side-effects that adds the following functions to a slice package:

Go Library for Competitive Programming with Generics

Go Library for Competitive Programming with Generics Go used to be a difficult language to use for competitive programming. However, with the introduc

Extended library functions using generics in Go.

Just few extended standard library functions for Golang using generics.

An in-memory key:value store/cache library written in Go 1.18 generics

go-generics-cache go-generics-cache is an in-memory key:value store/cache that is suitable for applications running on a single machine. This in-memor

A library that provides Go Generics friendly "optional" features.

go-optional A library that provides Go Generics friendly "optional" features. Synopsis some := optional.Some[int](123) fmt.Printf("%v\n", some.IsSome(

experimental promises in go1.18 with generics

async go a prototype of "promises" in go1.18. note: this is just an experiment used to test alternate patterns for dealing with asynchronous code in g

Go 1.18 generics use cases and examples

Go 1.18 generics use cases What are generics? See Type Parameters Proposal. How to run the examples? As of today, gotip is the simplest way to run the

N-tuple structures for Go with generics

tuple N-tuple structures for Go with generics Types 2-tuple Couple Pair Dyad 3-tuple Triple Triplet Triad 4-tuple Quadruple Quartet Quad Tetrad 5-tupl

Collection library using generics in Go

Collection Collection library using generics in Go Overview This is a library to provide useful collection data structures and methods for Gophers. Th

Goava: a toy project to explore Go generics

Goava This is a toy project to explore Go generics. Totally WIP. The idea is to create an in memory cache similar to Java's popular Guava cache. Gener

x/sync/singleflight but with Go 1.18 generics

singleflight This repo is a hard fork of golang.org/x/sync/singleflight that adds generics to the Group type so that there is no need for type asserti

Owner
David Roundy
David Roundy
CBuild build system - A tiny build system for C

cuild - CBuild A build system for C Building $ go build . Usage Similar to GNU Make, a file named "Cuildfile" is required. You have a few flags to us

Laurentino Luna 0 Jan 17, 2022
Build system and task runner for Go projects

Gilbert is task runner that aims to provide declarative way to define and run tasks like in other projects like Gradle, Maven and etc.

Gilbert 104 Nov 14, 2022
A Distributed Continuous Integration System from MongoDB

Evergreen Evergreen is a distributed continuous integration system built by MongoDB. It dynamically allocates hosts to run tasks in parallel across ma

Evergreen 369 Nov 19, 2022
Golang binaries compiled on-demand for your system

Go Binaries Go Binaries is an on-demand binary server, allowing non-Go users to quickly install tools written in Go without installing go itself, and

TJ Holowaychuk 760 Nov 9, 2022
Blueprint Build System For Golang

Blueprint Build System Blueprint is being archived on 2021 May 3. On 2021 May 3, we will be archiving the Blueprint project. This means it will not be

GengKapak WIP 0 Nov 20, 2021
go language generics system

Gotgo This document describes the third iteration of my attempt at a reasonable implementation of generics for go based on the idea of template packag

David Roundy 119 Dec 4, 2021
go language generics system

Gotgo This document describes the third iteration of my attempt at a reasonable implementation of generics for go based on the idea of template packag

David Roundy 119 Dec 4, 2021
Please is a cross-language high-performance extensible build system for reproducible multi-language builds.

Please is a cross-language build system with an emphasis on high performance, extensibility and reproducibility. It supports a number of popular languages and can automate nearly any aspect of your build process.

Thought Machine 2.2k Nov 24, 2022
Code Generation for Functional Programming, Concurrency and Generics in Golang

goderive goderive derives mundane golang functions that you do not want to maintain and keeps them up to date. It does this by parsing your go code fo

Walter Schulze 1k Nov 16, 2022
Elegant generics for Go

genny - Generics for Go Install: go get github.com/cheekybits/genny ===== (pron. Jenny) by Mat Ryer (@matryer) and Tyler Bunnell (@TylerJBunnell). Un

null 1.7k Nov 12, 2022