使用 Go 1.18 中的新泛型,我認為有可能創建一個 'Either[A,B]' 型別,它可以用來表示某物既可以是 A 型別,也可以是 B 型別。
您可能會使用它的情況是函式可能回傳兩個可能值之一作為結果(例如,一個用于“正常”結果,一個用于錯誤)。
我知道錯誤的“慣用” Go 將回傳“正常”值和錯誤值,為錯誤或值回傳 nil。但是......讓我有點困擾的是,我們本質上是在型別中說“這會回傳 A和B”,而我們真正想說的是“這會回傳 A或B”。
所以我想也許我們可以在這里做得更好,我認為這也可能是一個很好的練習來查看/測驗我們可以使用這些新泛型做什么的邊界。
可悲的是,盡我所能,到目前為止,我還沒有能夠解決這個練習并得到任何作業/編譯。從我失敗的一次嘗試中,這是我想以某種方式實作的介面:
//A value of type `Either[A,B]` holds one value which can be either of type A or type B.
type Either[A any, B any] interface {
// Call either one of two functions depending on whether the value is an A or B
// and return the result.
Switch[R any]( // <=== ERROR: interface methods must have no type parameters
onA func(a A) R),
onB func(b B) R),
) R
}
不幸的是,這很快就失敗了,因為 Go 不允許宣告這個介面。顯然是因為“介面方法必須沒有型別引數”。
我們如何解決這個限制?或者根本沒有辦法在 Go 中創建一個“型別”來準確表達“這個東西是/回傳 A 或 B”的想法(而不是 A 和 B 的元組)。
uj5u.com熱心網友回復:
可以將Either其建模為具有一個未匯出型別any/欄位的結構型別interface{}。型別引數將用于確保某種程度的編譯時型別安全:
type Either[A, B any] struct {
value any
}
func (e *Either[A,B]) SetA(a A) {
e.value = a
}
func (e *Either[A,B]) SetB(b B) {
e.value = b
}
func (e *Either[A,B]) IsA() bool {
_, ok := e.value.(A)
return ok
}
func (e *Either[A,B]) IsB() bool {
_, ok := e.value.(B)
return ok
}
如果Switch必須宣告為方法,則不能自行引數化R。附加型別引數必須在型別定義上宣告,但是這可能會使使用有點麻煩,因為R必須在實體化時選擇 then。
一個獨立的函式似乎更好——在同一個包中,訪問未匯出的欄位:
func Switch[A,B,R any](e *Either[A,B], onA func(A) R, onB func(B) R) R {
switch v := e.value.(type) {
case A:
return onA(v)
case B:
return onB(v)
}
}
帶有一些代碼和用法的游樂場:https ://go.dev/play/p/g-NmE4KZVq2
uj5u.com熱心網友回復:
如果我必須這樣做,我會查找一種函式式編程語言(如 OCaml)并仿制他們的任一型別的解決方案。
package main
import (
"errors"
"fmt"
"os"
)
type Optional[T any] interface {
get() (T, error)
}
type None[T any] struct {
}
func (None[T]) get() (T, error) {
var data T
return data, errors.New("No data present in None")
}
type Some[T any] struct {
data T
}
func (s Some[T]) get() (T, error) {
return s.data, nil
}
func CreateNone[T any]() Optional[T] {
return None[T]{}
}
func CreateSome[T any](data T) Optional[T] {
return Some[T]{data}
}
type Either[A, B any] interface {
is_left() bool
is_right() bool
find_left() Optional[A]
find_right() Optional[B]
}
type Left[A, B any] struct {
data A
}
func (l Left[A, B]) is_left() bool {
return true
}
func (l Left[A, B]) is_right() bool {
return false
}
func left[A, B any](data A) Either[A, B] {
return Left[A, B]{data}
}
func (l Left[A, B]) find_left() Optional[A] {
return CreateSome(l.data)
}
func (l Left[A, B]) find_right() Optional[B] {
return CreateNone[B]()
}
type Right[A, B any] struct {
data B
}
func (r Right[A, B]) is_left() bool {
return false
}
func (r Right[A, B]) is_right() bool {
return true
}
func right[A, B any](data B) Either[A, B] {
return Right[A, B]{data}
}
func (r Right[A, B]) find_left() Optional[A] {
return CreateNone[A]()
}
func (r Right[A, B]) find_right() Optional[B] {
return CreateSome(r.data)
}
func main() {
var e1 Either[int, string] = left[int, string](4143)
var e2 Either[int, string] = right[int, string]("G4143")
fmt.Println(e1)
fmt.Println(e2)
if e1.is_left() {
if l, err := e1.find_left().get(); err == nil {
fmt.Printf("The int is: %d\n", l)
} else {
fmt.Fprintln(os.Stderr, err)
}
}
if e2.is_right() {
if r, err := e2.find_right().get(); err == nil {
fmt.Printf("The string is: %s\n", r)
} else {
fmt.Fprintln(os.Stderr, err)
}
}
}
uj5u.com熱心網友回復:
我終于找到了一個解決方案。關鍵是將“Either”型別定義為“結構”而不是介面。
type Either[A any, B any] struct {
isA bool
a A
b B
}
func Switch[A any, B any, R any](either Either[A, B],
onA func(a A) R,
onB func(b B) R,
) R {
if either.isA {
return onA(either.a)
} else {
return onB(either.b)
}
}
func MakeA[A any, B any](a A) Either[A, B] {
var result Either[A, B]
result.isA = true
result.a = a
return result
}
func MakeB[A any, B any](b B) Either[A, B] {
... similar to MakeA...
}
這行得通,但是實際上仍然在使用“類似元組”的實作的“代價”是我們同時存盤了 A和B,但確保只能通過公共 API 使用其中一個。
鑒于 Go 對我們的限制,我懷疑這是我們能做的最好的事情。
如果有人有一個基本上不使用“元組”來表示“聯合”的“解決方法”。我認為這是一個更好的答案。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/460198.html
上一篇:來自串列的函式中的泛型型別推斷
