How to define two separate types for arbitrary precision decimals so they can only be be used with the same type and not assigned be to each other - go

My current code base defines two types like this:
type Price uint64
type Quantity uint64
This works out nicely as I can't accidentally pass a Price type into a Quantity or else the compiler will complain.
I now need to switch the implementation from uint64 to an arbitrary precision decimal using the shopspring/decimal library.
I'd like the following requirements that worked from the previous uint64 implementation:
If I pass a Price to a function expecting a Quantity and vice-versa, the compiler will complain
I can do calculations (such as calling Add) between two Quantity's without any extra boilerplate, but for doing calculations between different types (such as multiplying a Price by a Quantity), I need to explicitly allow it by doing something such as casting.
I'd like to not have duplicate code such as defining every single method I want to use separately for each type (even if it delegates to a common implementation)
I've tried 3 different implementations, but none of them work right. Is there any approach that I am missing that would do what I want? If not, what is the recommended way to do things?
Approach 1
type Price decimal.Decimal
type Quantity decimal.Decimal
This implementations means I can't use methods on decimal.Decimal (such as Add()) for variables of type Price since according to the Go spec "It does not inherit any methods bound to the given type".
Approach 2
I can use a type alias like this:
type Price = decimal.Decimal
type Quantity = decimal.Decimal
but in this case I can pass a Price into a function expecting a Quantity so I don't get the type protection. Some documentation says the type aliases are mainly for helping during refactoring.
Approach 3
I can try to use an embedded type:
type Quantity struct {
decimal.Decimal
}
This works in most cases, but in this case:
qty.Add(qty2)
qty2 isn't a decimal.Decimal so I'd have to do ugly things like
qty.Add(qty2.Decimal)

You can use this approach with generics. It's easier to do for a type you write yourself. If you want to implement it with an external type, you will need a wrapper.
Example:
type Number[K any] uint64
func (n Number[K]) Add(n2 Number[K]) Number[K] {
return n + n2
}
// These are dummy types used as parameters to differentiate Number types.
type (
price struct{}
quantity struct{}
)
func main() {
var somePrice Number[price]
var someQuantity Number[quantity]
// no error
somePrice.Add(somePrice)
// cannot use someQuantity (variable of type Number[quantity]) as type Number[price] in argument to somePrice.Add
somePrice.Add(someQuantity)
}
Now if you want to do this for an external type like decimal.Decimal, which you can't edit the source for to make it work like this, you must write wrappers for any methods where you need the parameter types to be covariant with the receiver type.
Example, here I'm assuming you're using the https://github.com/shopspring/decimal library:
package main
import "github.com/shopspring/decimal"
type Number[K any] struct{ decimal.Decimal }
// Wrapper to enforce covariant type among receiver, parameters and return.
func (n Number[K]) Add(d2 Number[K]) Number[K] {
return Number[K]{n.Decimal.Add(d2.Decimal)}
}
// These are dummy types used as parameters to differentiate Number types.
type (
price struct{}
quantity struct{}
)
func main() {
var somePrice Number[price]
var someQuantity Number[quantity]
// no error
somePrice.Add(somePrice)
// cannot use someQuantity (variable of type Number[quantity]) as type Number[price] in argument to somePrice.Add
somePrice.Add(someQuantity)
}
You will need a wrapper for each method with covariant types.
Alternatively, you can make your own library or fork the existing one and add this feature directly with the method in the first example:
For example, your fork of decimal.go could look like:
// +++++++
type Decimal[K any] struct { ... }
// +++ +++ +++
func (d Decimal[K]) Add(d2 Decimal[K]) Decimal[K] { ... }

Related

go generics: how to declare a type parameter compatible with another type parameter

I'm looking for a way to declare type compatibility between type parameters in Go generics constraints.
More specifically, I need to say some type T is compatible with another type U. For instance, T is a pointer to a struct that implements the interface U.
Below is a concrete example of what I want to accomplish:
NOTE: Please, do not answer with alternative ways to implement "array prepend". I've only used it as a concrete application of the problem I'm looking to solve. Focusing on the specific example digresses the conversation.
func Prepend[T any](array []T, values ...T) []T {
if len(values) < 1 { return array }
result := make([]T, len(values) + len(array))
copy(result, values)
copy(result[len(values):], array)
return result
}
The above function can be called to append elements of a given type T to an array of the same type, so the code below works just fine:
type Foo struct{ x int }
func (self *Foo) String() string { return fmt.Sprintf("foo#%d", self.x) }
func grow(array []*Foo) []*Foo {
return Prepend(array, &Foo{x: len(array)})
}
If the array type is different than the elements being added (say, an interface implemented by the elements' type), the code fails to compile (as expected) with type *Foo of &Foo{…} does not match inferred type Base for T:
type Base interface { fmt.Stringer }
type Foo struct{ x int }
func (self *Foo) String() string { return fmt.Sprintf("foo#%d", self.x) }
func grow(array []Base) []Base {
return Prepend(array, &Foo{x: len(array)})
}
The intuitive solution to that is to change the type parameters for Prepend so that array and values have different, but compatible types. That's the part I don't know how to express in Go.
For instance, the code below doesn't work (as expected) because the types of array and values are independent of each other. Similar code would work with C++ templates since the compatibility is validated after template instantiation (similar to duck typing). The Go compiler gives out the error invalid argument: arguments to copy result (variable of type []A) and values (variable of type []T) have different element types A and T:
func Prepend[A any, T any](array []A, values ...T) []A {
if len(values) < 1 { return array }
result := make([]A, len(values) + len(array))
copy(result, values)
copy(result[len(values):], array)
return result
}
I've tried making the type T compatible with A with the constraint ~A, but Go doesn't like a type parameter used as type of a constraint, giving out the error type in term ~A cannot be a type parameter:
func Prepend[A any, T ~A](array []A, values ...T) []A {
What's the proper way to declare this type compatibility as generics constraints without resorting to reflection?
This is a limitation of Go's type parameter inference, which is the system that tries to automatically insert type parameters in cases where you don't define them explicitly. Try adding in the type parameter explicitly, and you'll see that it works. For example:
// This works.
func grow(array []Base) []Base {
return Prepend[Base](array, &Foo{x: len(array)})
}
You can also try explicitly converting the *Foo value to a Base interface. For example:
// This works too.
func grow(array []Base) []Base {
return Prepend(array, Base(&Foo{x: len(array)}))
}
Explanation
First, you should bear in mind that the "proper" use of type parameters is to always include them explicitly. The option to omit the type parameter list is considered a "nice to have", but not intended to cover all use cases.
From the blog post An Introduction To Generics:
Type inference in practice
The exact details of how type inference works are complicated, but using it is not: type inference either succeeds or fails. If it succeeds, type arguments can be omitted, and calling generic functions looks no different than calling ordinary functions. If type inference fails, the compiler will give an error message, and in those cases we can just provide the necessary type arguments.
In adding type inference to the language we’ve tried to strike a balance between inference power and complexity. We want to ensure that when the compiler infers types, those types are never surprising. We’ve tried to be careful to err on the side of failing to infer a type rather than on the side of inferring the wrong type. We probably have not gotten it entirely right, and we may continue to refine it in future releases. The effect will be that more programs can be written without explicit type arguments. Programs that don’t need type arguments today won’t need them tomorrow either.
In other words, type inference may improve over time, but you should expect it to be limited.
In this case:
// This works.
func grow(array []*Foo) []*Foo {
return Prepend(array, &Foo{x: len(array)})
}
It is relatively simple for the compiler to match that the argument types of []*Foo and *Foo match the pattern []T and ...T by substitutingT = *Foo.
So why does the plain solution you gave first not work?
// Why does this not work?
func grow(array []Base) []Base {
return Prepend(array, &Foo{x: len(array)})
}
To make []Base and *Foo match the pattern []T and ...T, just substituting T = *Foo or T = Base provides no apparent match. You have to apply the rule that *Foo is assignable to the type Base to see that T = Base works. Apparently the inference system doesn't go the extra mile to try to figure that out, so it fails here.

Is there a way we can ensure passed values have certain fields using generics?

I am trying to define a generic function in Go that accepts values that have certain fields, for example, ID int. I have tried several approaches but none seems to work. Here is an example of what I have tried.
package main
import (
"fmt"
)
func Print[T IDer](s T) {
fmt.Print(s.ID)
}
func main() {
Print(Person{3, "Test"})
}
type IDer interface {
~struct{ ID int }
}
type Person struct {
ID int
Name string
}
type Store struct {
ID int
Domain string
}
And here is the playground link: https://gotipplay.golang.org/p/2I4RsUCwagF
In the example above, I want to guarantee every value passed to the Print function has a property ID int, which is also accessible in the function. Is there any way I can achieve this is Go without defining a method in an interface (e.g., GetID() int)?
Is there any way I can achieve this is Go without defining a method in an interface (e.g., GetID() int)?
No, you have to define the method in an interface.
The generics implementation in Go 1.18 doesn't have support for structural types, even though the original type parameters proposal suggests it would. For accessing common fields in a union instead see also this explanation.
Although, I think it's worth it to point out a misconception that can easily arise from your example: the meaning of the approximation ~T (tilde-type) means "the set of types whose underlying type is T.
Now, when you write:
~struct{ ID int }
this means types whose underlying type is exactly struct{ ID int }. No matter what, this does not include structs that have the field ID int and something else. E.g. the underlying type of type Foo struct { ID int; Name string } is struct { ID int; Name string }, and not struct{ ID int }, so that wouldn't satisfy the constraint anyway.
The current time param implementation doesn't have syntax to specify partial struct types. I recall a proposal to add field terms in interface constraints (along with type terms and methods), something on the line:
type IDer interface {
ID int
}
which would enable what you are trying to do without breaking the meaning of the tilde ~. But this won't be included in Go 1.18.

Can I get a variable of a type based on reflect.Type [duplicate]

I have a function which takes an interface, like this:
func method(data interface{})
.. because I need to process different structs which have common fields/methods. In this function I use data tens or hundreds of times, in different places. It's really unpleasant to add switch a.(type) { case .. case .. all the time.
Is there a way to create a variable with just one switch with needed type and then just use this variable everywhere later? Something like:
var a .... // something here
switch data.(type) {
case *Struct1:
a = data.(*Struct1)
case *Struct2:
a = data.(*Struct2)
}
// Continue with 'a' only
a.Param = 15
fmt.Println(a.String())
Go is a statically typed language, the type of a must be known at compile time. And since Go does not support generics yet, you can't do what you want.
Try to come up with some other solution, e.g. abstract away the things you want to do with a into an interface, and have the concrete types implement that interface. Then a can be a variable of this interface type, and you can call methods of it.
If you can achieve this, actually you can even change the parameter of the data type to this interface, and no type assertion or type switch is needed.
Alternatively you could use reflection to access common fields (either for get or set) identified by their name, but reflection provides no compile-time guarantee, and it's usually less efficient. For an example how to do that, see this question: Assert interface to its type
You can't do what you ask for in your question directly, go is statically typed, so you can't have one variable that can hold different types, and still access that variable as if it is typed.
If you're only working on the common struct fields in your method, you are perhaps better off gathering all the common variables in its own struct, illustrated below as the commons struct and have your method take that type as an argument
package main
import (
"fmt"
)
type commons struct {
name string
age int
}
type structA struct {
commons
other_stuff int
}
type structB struct {
commons
foo string
}
func method(c* commons) {
fmt.Println(c)
c.age +=1
}
func main() {
a := structA{commons{"foo", 44}, 1}
b := structB{commons{"bar", 33}, "test"}
method(&a.commons)
method(&b.commons)
fmt.Println(a)
}
Go playground
I can't figure out what is your real goal but if the "method" you want to write handles common fields from similar structures, and you cannot fix original structures using Type Embedding, as #nos said above, then you can try to make another structure for method-internal use:
var v Vehicle // with common fields
switch data.(type) {
case *Car:
v.Handle = data.(*Car).Handle // or CircleHandle
case *Motorcycle:
v.Handle = data.(*Motorcycle).Handle // or BarHandle
}
v.Degree = 15
v.Speed = 50
v.Direction = "left"
v.Style = "rough"
/// so many things on `v`...
steering(v)
I think it is not a good approach but sometimes... :-)

How to create a variable with needed type instead of type assertion

I have a function which takes an interface, like this:
func method(data interface{})
.. because I need to process different structs which have common fields/methods. In this function I use data tens or hundreds of times, in different places. It's really unpleasant to add switch a.(type) { case .. case .. all the time.
Is there a way to create a variable with just one switch with needed type and then just use this variable everywhere later? Something like:
var a .... // something here
switch data.(type) {
case *Struct1:
a = data.(*Struct1)
case *Struct2:
a = data.(*Struct2)
}
// Continue with 'a' only
a.Param = 15
fmt.Println(a.String())
Go is a statically typed language, the type of a must be known at compile time. And since Go does not support generics yet, you can't do what you want.
Try to come up with some other solution, e.g. abstract away the things you want to do with a into an interface, and have the concrete types implement that interface. Then a can be a variable of this interface type, and you can call methods of it.
If you can achieve this, actually you can even change the parameter of the data type to this interface, and no type assertion or type switch is needed.
Alternatively you could use reflection to access common fields (either for get or set) identified by their name, but reflection provides no compile-time guarantee, and it's usually less efficient. For an example how to do that, see this question: Assert interface to its type
You can't do what you ask for in your question directly, go is statically typed, so you can't have one variable that can hold different types, and still access that variable as if it is typed.
If you're only working on the common struct fields in your method, you are perhaps better off gathering all the common variables in its own struct, illustrated below as the commons struct and have your method take that type as an argument
package main
import (
"fmt"
)
type commons struct {
name string
age int
}
type structA struct {
commons
other_stuff int
}
type structB struct {
commons
foo string
}
func method(c* commons) {
fmt.Println(c)
c.age +=1
}
func main() {
a := structA{commons{"foo", 44}, 1}
b := structB{commons{"bar", 33}, "test"}
method(&a.commons)
method(&b.commons)
fmt.Println(a)
}
Go playground
I can't figure out what is your real goal but if the "method" you want to write handles common fields from similar structures, and you cannot fix original structures using Type Embedding, as #nos said above, then you can try to make another structure for method-internal use:
var v Vehicle // with common fields
switch data.(type) {
case *Car:
v.Handle = data.(*Car).Handle // or CircleHandle
case *Motorcycle:
v.Handle = data.(*Motorcycle).Handle // or BarHandle
}
v.Degree = 15
v.Speed = 50
v.Direction = "left"
v.Style = "rough"
/// so many things on `v`...
steering(v)
I think it is not a good approach but sometimes... :-)

Why create go types based on other?

what is the purpose of defining new types in go:
type NewType OldType
since NewType have only methods declarations, so:
var x NewType
can store also OldType 'objects'. Are there any advantages?
The reason behind naming types in general is fairly straightforward, and is much the same in most languages - being able to name complex types, like:
type Person struct{
name String
age uint8
}
However, naming a type like you described, which I'll call "type aliasing" (not sure if this is used by anyone else, but it's the term I tend to use), doesn't give you the above-mentioned advantage. What it does give you, however, is the ability to add methods to existing types. Go disallows you from adding methods to existing types that you did not define yourself (ie, built-in types or types defined in other packages), so aliasing allows you to pretend that you did define them yourself, and thus add methods to them. Another good way to think about it is like a much more concise version of creating a wrapper type (as you would in an OO language like Java, for example).
So, let's say that I wanted to be able use integers as errors. In Go, the error interface simply requires a method called "Error" which returns a string. Using type aliasing, I could do:
type errorCode int
func (e errorCode) Error() string {
return fmt.Sprintf("%d", e)
}
...and I could use integer error codes. By contrast, if I tried the following, I would get an error:
func (e int) Error() string {
return fmt.Sprintf("%d", e)
}
To demonstrate, check out this implementation:
http://play.golang.org/p/9NO6Lcdsbq
Just to clarify (because my use of the word "alias" may be misleading), two types which are otherwise equivalent (for example, int and errorCode in the above example) are not interchangeable. The Go type system treats them as fundamentally different types, although you may be able to type-cast between them.
The Go Programming Language Specification
Types
A type determines the set of values and operations specific to values
of that type.
You want identify a specific set of values and operations.
For example,
package main
import "fmt"
type Coordinate float64
type Point struct {
x, y Coordinate
}
func (p *Point) Move(dx, dy Coordinate) {
p.x += dx
p.y += dy
}
func main() {
var p = Point{3.14159, 2.718}
fmt.Println(p)
p.Move(-1, +1)
fmt.Println(p)
}
Output:
{3.14159 2.718}
{2.14159 3.718}

Resources