Skip to content

Commit

Permalink
content: add generic option
Browse files Browse the repository at this point in the history
  • Loading branch information
changkun committed Apr 11, 2022
1 parent a7572b8 commit a134537
Show file tree
Hide file tree
Showing 6 changed files with 729 additions and 0 deletions.
53 changes: 53 additions & 0 deletions content/assets/generic-option/1pattern/option.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2022 The golang.design Initiative Authors.
// All rights reserved. Use of this source code is governed
// by a MIT license that can be found in the LICENSE file.
//
// Written by Changkun Ou <changkun.de>

package main

import "fmt"

type A struct {
v1 int
// Removed, now moved to v3.
// v2 int
v3 int
}

type Option func(*A)

func V1(v1 int) Option {
return func(a *A) {
a.v1 = v1
}
}

// Deprecated: Use V3 instead.
func V2(v2 int) Option {
return func(a *A) {
// no effects anymore
// a.v2 = v2
}
}

func V3(v3 int) Option {
return func(a *A) {
a.v3 = v3
}
}

func NewA(opts ...Option) *A {
a := &A{}
for _, opt := range opts {
opt(a)
}
return a
}

func main() {
fmt.Printf("%#v\n", NewA()) // &main.A{v1:0, v2:0}
fmt.Printf("%#v\n", NewA(V1(42))) // &main.A{v1:42, v2:0}
fmt.Printf("%#v\n", NewA(V2(42))) // &main.A{v1:0, v2:0}
fmt.Printf("%#v\n", NewA(V1(42), V2(42))) // &main.A{v1:42, v2:0}
}
66 changes: 66 additions & 0 deletions content/assets/generic-option/2problem/option.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2022 The golang.design Initiative Authors.
// All rights reserved. Use of this source code is governed
// by a MIT license that can be found in the LICENSE file.
//
// Written by Changkun Ou <changkun.de>

package main

import "fmt"

type A struct {
v1 int
}

type B struct {
v1 int
v2 int
}

type OptionA func(a *A)
type OptionB func(a *B)

func V1ForA(v1 int) OptionA {
return func(a *A) {
a.v1 = v1
}
}

func V1ForB(v1 int) OptionB {
return func(b *B) {
b.v1 = v1
}
}

func V2ForB(v2 int) OptionB {
return func(b *B) {
b.v2 = v2
}
}

func NewA(opts ...OptionA) *A {
a := &A{}

for _, opt := range opts {
opt(a)
}
return a
}

func NewB(opts ...OptionB) *B {
b := &B{}

for _, opt := range opts {
opt(b)
}
return b
}

func main() {
fmt.Printf("%#v\n", NewA()) // &main.A{v1:0}
fmt.Printf("%#v\n", NewA(V1ForA(42))) // &main.A{v1:42}
fmt.Printf("%#v\n", NewB()) // &main.B{v1:0, v2:0}
fmt.Printf("%#v\n", NewB(V1ForB(42))) // &main.B{v1:42, v2:0}
fmt.Printf("%#v\n", NewB(V2ForB(42))) // &main.B{v1:0, v2:42}
fmt.Printf("%#v\n", NewB(V1ForB(42), V2ForB(42))) // &main.B{v1:42, v2:42}
}
77 changes: 77 additions & 0 deletions content/assets/generic-option/3interface/option.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright 2022 The golang.design Initiative Authors.
// All rights reserved. Use of this source code is governed
// by a MIT license that can be found in the LICENSE file.
//
// Written by Changkun Ou <changkun.de>

package main

import "fmt"

type A struct {
v1 int
}

type B struct {
v1 int
v2 int
}

type Common interface {
/* ... */
}

type Option func(c Common)

func V1(v1 int) Option {
return func(c Common) {
switch x := c.(type) {
case *A:
x.v1 = v1
case *B:
x.v1 = v1
default:
panic("unexpected use")
}
}
}

func V2(v2 int) Option {
return func(c Common) {
switch x := c.(type) {
case *B:
x.v2 = v2
default:
panic("unexpected use")
}
}
}

func NewA(opts ...Option) *A {
a := &A{}

for _, opt := range opts {
opt(a)
}
return a
}

func NewB(opts ...Option) *B {
b := &B{}

for _, opt := range opts {
opt(b)
}
return b
}

func main() {
fmt.Printf("%#v\n", NewA()) // &main.A{v1:0}
fmt.Printf("%#v\n", NewA(V1(42))) // &main.A{v1:42}
fmt.Printf("%#v\n", NewB()) // &main.B{v1:0, v2:0}
fmt.Printf("%#v\n", NewB(V1(42))) // &main.B{v1:42, v2:0}
fmt.Printf("%#v\n", NewB(V2(42))) // &main.B{v1:0, v2:42}
fmt.Printf("%#v\n", NewB(V1(42), V2(42))) // &main.B{v1:42, v2:42}

// fmt.Printf("%#v\n", NewA(V2(42))) // ERROR: panic
}
74 changes: 74 additions & 0 deletions content/assets/generic-option/4generic/option.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2022 The golang.design Initiative Authors.
// All rights reserved. Use of this source code is governed
// by a MIT license that can be found in the LICENSE file.
//
// Written by Changkun Ou <changkun.de>

package main

import "fmt"

type A struct {
v1 int
}

type B struct {
v1 int
v2 int
}

type Option[T A | B] func(a *T)

func V1[T A | B](v1 int) Option[T] {
return func(a *T) {
switch x := any(a).(type) {
case *A:
x.v1 = v1
case *B:
x.v1 = v1
default:
panic("unexpected use")
}
}
}

func V2[T B](v2 int) Option[T] {
return func(a *T) {
switch x := any(a).(type) {
case *B:
x.v2 = v2
default:
panic("unexpected use")
}
}
}

func NewA[T A](opts ...Option[T]) *T {
t := new(T)
for _, opt := range opts {
opt(t)
}
return t
}

func NewB[T B](opts ...Option[T]) *T {
t := new(T)
for _, opt := range opts {
opt(t)
}
return t
}

func main() {
fmt.Printf("%#v\n", NewA()) // &main.A{v1:0}
fmt.Printf("%#v\n", NewA(V1[A](42))) // &main.A{v1:42}
fmt.Printf("%#v\n", NewB()) // &main.B{v1:0, v2:0}
fmt.Printf("%#v\n", NewB(V1[B](42))) // &main.B{v1:42, v2:0}
fmt.Printf("%#v\n", NewB(V2[B](42))) // &main.B{v1:0, v2:42}
fmt.Printf("%#v\n", NewB(V1[B](42), V2[B](42))) // &main.B{v1:42, v2:42}

// _ = NewA(V2[B](42)) // ERROR: B does not implement A
// _ = NewA(V2[A](42)) // ERROR: A does not implement B
// _ = NewB(V1[A](42), V2[B](42)) // ERROR: type Option[B] of V2[B](42) does not match inferred type Option[A] for Option[T]
// _ = NewB(V1[B](42), V2[A](42)) // ERROR: type Option[A] of V2[A](42) does not match inferred type Option[B] for Option[T]
}
3 changes: 3 additions & 0 deletions content/assets/generic-option/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module generic-option

go 1.18
Loading

0 comments on commit a134537

Please sign in to comment.