Skip to content

Instantly share code, notes, and snippets.

@deanveloper
Last active October 30, 2018 17:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save deanveloper/c495da6b9263b35f98b773e34bd41104 to your computer and use it in GitHub Desktop.
Save deanveloper/c495da6b9263b35f98b773e34bd41104 to your computer and use it in GitHub Desktop.

It is unclear how to represent operators using interface methods. We considered syntaxes like +(T, T) T, but that is confusing and repetitive. Also, a minor point, but ==(T, T) bool does not correspond to the == operator, which returns an untyped boolean value, not bool. We also considered writing simply + or ==. That seems to work but unfortunately the semicolon insertion rules require writing a semicolon after each operator at the end of a line. Using contracts that look like functions gives us a familiar syntax at the cost of some repetition. These are not fatal problems, but they are difficulties.

https://go.googlesource.com/proposal/+/master/design/go2draft-contracts.md#why-not-use-interfaces-instead-of-contracts

Do these problems really justify creating an entirely new construct that has a very similar purpose to something we already have?

Is there really no other solution? We could do operator[<op>] or something similar instead...

Some common interfaces people may be using

// Describes bool, complex, pointers, channels, interfaces,
// some structs, some arrays, strings, floats, and ints.
type Comparable interface {
  operator[==]
  operator[!=]
}

// describes strings, floats, ints
type Orderable interface {
  Comparable
  operator[<]
  operator[>]
  operator[<=]
  operator[>=]
}

// describes floats and ints
type RealNumeric interface {
  Comparable
  operator[+]
  operator[-]
  operator[*]
  operator[/]
  operator[%]
}

// describes ints
type BinaryNumeric interface {
  RealNumeric
  operator[<<]
  operator[>>]
  operator[&]
  operator[|]
  operator[^]
  operator[&^]
}

For a Graph interface:

type Edge interface {
  Nodes() (Node, Node)
}
type Node interface {
  Edges() []EdgeInterface
}

type Graph(type N Node, E Edge) struct {
  // ...
}

func (type N Node, E Edge) NewGraph(init []N) *Graph(N, E) {
  // ....
}

For a Sum method:

type Addable interface {
  operator[+]
}

func (type T Addable) Sum(t ...T) {
  var sum T
  for _, elem := range t {
    sum += elem // since + is defined for T, we may do this
  }
  return sum
}

func main() {
  Sum(5, 6, 9) // returns 20
  
  Sum("woah", "strings") // returns "woahstrings"
}

Doing this we could also redefine a lot of the things in builtin.go

type bool comparable
type complex64 comparable
type complex128 comparable

type string orderable

type float32 realnumeric
type float64 realnumeric

type int binarynumeric
type int8 binarynumeric
type int16 binarynumeric
type int32 binarynumeric
type int64 binarynumeric
type uint binarynumeric
type uint8 binarynumeric
type uint16 binarynumeric
type uint32 binarynumeric
type uint64 binarynumeric
type uintptr binarynumeric

type rune = int32
type byte = uint8

The only issue I can find now is that there is no way to generalize complex64 and complex128. You are able to do this with contracts using contract Complex(c C) { imag(c) }, but there is no way to do this in Go. But honestly, it might be best to just remove complex, add operator functions, and create a math/complex package.

@jimmyfrasche
Copy link

What do operators in interfaces mean when used as regular interfaces rather than as constraints?

var a, b Orderable = 1, "hi"
_ = a < b
var c Orderable
_ = a < c

@deanveloper
Copy link
Author

Hmm, you are right about that, and I never considered it. That's a really simple case that I never really thought of. I might make a new proposal which addresses this, I have some ideas though.

I think contracts are okay, but they are very similar to interfaces. One of my favorite things about Go is that there are very few ways to do the same thing, so when we have both contracts and interfaces, it kinda throws me for a loop.

@johnw42
Copy link

johnw42 commented Sep 20, 2018

I think this proposal could be combined with this one, which uses pre-defined contracts for language primitives. Just make those contracts into special built-in interfaces. The compiler knows which operators are valid for a given interface, so there's no need to have any syntax for specifying them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment