How to statically limit function arguments to a subset of values - go

How does one statically constrain a function argument to a subset of values for the required type?
The set of values would be a small set defined in a package. It would be nice to have it be a compile-time check instead of runtime.
The only way that I've been able to figure out is like this:
package foo
// subset of values
const A = foo_val(0)
const B = foo_val(1)
const C = foo_val(2)
// local interface used for constraint
type foo_iface interface {
get_foo() foo_val
}
// type that implements the foo_iface interface
type foo_val int
func (self foo_val) get_foo() foo_val {
return self
}
// function that requires A, B or C
func Bar(val foo_iface) {
// do something with `val` knowing it must be A, B or C
}
So now the user of a package is unable to substitute any other value in place of A, B or C.
package main
import "foo"
func main() {
foo.Bar(foo.A) // OK
foo.Bar(4) // compile-time error
}
But this seems like quite a lot of code to accomplish this seemingly simple task. I have a feeling that I've overcomplicated things and missed some feature in the language.
Does the language have some feature that would accomplish the same thing in a terse syntax?

Go can't do this (I don't think, I don't think a few months makes me experienced)
ADA can, and C++ can sometimes-but-not-cleanly (constexpr and static_assert).
BUT the real question/point is here, why does it matter? I play with Go with GCC as the compiler and GCC is REALLY smart, especially with LTO, constant propigation is one of the easiest optimisations to apply and it wont bother with the check (you are (what we'd call in C anyway) statically initialising A B and C, GCC optimises this (if it has a definition of the functions, with LTO it does))
Now that's a bit off topic so I'll stop with that mashed up blob but tests for sane-ness of a value are good unless your program is CPU bound don't worry about it.
ALWAYS write what it easier to read, you'll be thankful you did later
So do your runtime checks, if the compiler has enough info to hand it wont bother doing them if it can deduce (prove) they wont throw, with constant values like that it'll spot it eaisly.
Addendum
It's difficult to do compile time checks, constexpr in c++ for example is very limiting (everything it touches must also be constexpr and such) - it doesn't play nicely with normal code.
Suppose a value comes from user input? That check has to be at runtime, it'd be silly (and violate DRY) if you wrote two sets of constraints (however that'd work), one for compile one for run.
The best we can do is make the compiler REALLY really smart, and GCC is. I'm sure others are good too ('cept MSs one, I've never heard a compliment about it, but the authors are smart because they wrote a c++ parser for a start!)

A slightly different approach that may suit your needs is to make the function a method of the type and export the set of valid values but not a way to construct new values.
For example:
package foo
import (
"fmt"
)
// subset of values
const A = fooVal(0)
const B = fooVal(1)
const C = fooVal(2)
// type that implements the foo_iface interface
type fooVal int
// function that requires A, B or C
func (val fooVal) Bar() {
fmt.Println(val)
}
Used by:
package main
import "test/foo"
func main() {
foo.A.Bar() // OK, prints 0
foo.B.Bar() // OK, prints 1
foo.C.Bar() // OK, prints 2
foo.4.Bar() // syntax error: unexpected literal .4
E := foo.fooVal(5) // cannot refer to unexported name foo.fooVal
}

Related

Can I include formulas in a struct somehow?

I am trying to create a struct which uses a formula to automatically create data in one of the struct fields when the other two values are entered.
For example, I want to create a 2D rectangular room with Length and Width which are values that are entered. I would then like to include the formula Area = Length * Width in the struct.
Have tried and just get a syntax error :
syntax error: unexpected =, expecting semicolon or newline or }
// CURRENT CODE
type room struct {
L int
W int
A int
}
// WOULD LIKE IT TO BE
type room struct {
L int
W int
A int = room.L*room.H
}
Since A is invariant, this would be a good fit for a function, not a field.
type room struct {
L int
W int
}
func (r *room) area() int {
return r.L * r.W
}
If you would like to keep A as a Field, you can optionally preform the computation in a constructor.
type room struct {
L int
W int
A int
}
func newRoom(length, width, int) room {
return room{
L: length,
W: width,
A: length * width,
}
}
If you think about what you're after, you'll see that basically your desire to "not add unnecessary code" is really about not writing any code by hand, rather than not executing any code: sure, if the type definition
type room struct {
L int
W int
A int = room.L*room.H
}
could be possible in Go, that would mean the Go compiler would have make arrangements so than any code like this
var r room
r.L = 42
is compiled in a way to implicitly mutate r.A.
In other words, the compiler must make sure that any modification of either L or W fields of any variable of type room in a program would also perform a calculaton and update the field A of each such variable.
This poses several problems:
What if your formula is trickier—like, say, A int = room.L/room.W?
First, given the casual Go rules for zero values of type int,
an innocent declaration var r room would immediately crash the program because of the integer division by zero performed by the code inserted by the compiler to force the invariant being discussed.
Second, even if we would invent a questionable rule of not calculating a formula on mere declarations (which, in Go, are also initializations), the problem would remain: what would happen in the following scenario?
var r room
r.L = 42
As you can see, even if the compiler would not make the program crash on the first line, it would have to arrange for that on the second.
Sure, we could add another questionable rule to sidestep the problem: either somehow "mark" each field as "explicitly set" or require the user to provide an explicit "constructor" for such types "armed" with a "formula".
Either solution stinks in its own way: tracing write field access incurs performance costs (some fields now have a hidden flag which takes up space, and each access of such fields spends extra CPU counts), and having constructors goes again one of the cornerstone principles of the Go design: to have as little magic as possible.
The formula creates a hidden write.
This may not be obvious until you start writing "harder-core" Go programs for tasks it shines at—highly concurrent code with lots of simultaneously working goroutines,—but when you do you're forced to think about shared state and the ways it's mutated and—consequently—on the ways such mutations are synchronized to keep the program correct.
So, let's suppose we protect access to either W or L with a mutex; how would the compiler make sure mutation of A is also proteted given that mutex operations are explicit (that is, a programmer explicitly codes locking/unlocking operations)?
(A problem somewhat related to the previous one.)
What if "the formula" does "interesting things"—such as accessing/mutating external state?
This could be anything from accessing global variables to querying databases to working with a filesystems to exchanges over IPC or via networking protocols.
And this all could be very innocently-looking, like A int = room.L * room.W * getCoefficient() where all the nifty details are hidden in that getCoefficient() call.
Sure, we, again, could work-around this by imposing an arbitrary limit on the compiler to only allow explicit access to the fields of the same enclosing type and only allow them to participate in simple expressions with no function calls or some "whitelisted" subset of them such as math.Abs or whatever.
This clearly reduces the usefulness of the feature while greatly complicating the language.
What if "the formula" has non-linear complexity?
Suppose, the formula is O(N³) with regard to the value of W.
Then setting W on a value to 0 would be processed almost instantly but setting it to 10000 would slow the program down quite noticeably, and both of these outcomes would result form a seemingly not too different statements: r.W = 0 vs r.W = 10000.
This, again, goes agains the principle of having as little magic as possible.
Why would we ony allow such things on struct types and not on arbitrary variables—prodived they are all in the same lexical scope?
This looks like another arbitrary restriction.
And another—supposedly—the most obvious problem is what should happen when the programmer goes like
var r room
r.L = 2 // r.A is now 2×0=0
r.W = 5 // r.A is now 2×5=10
r.A = 42 // The invariant r.A = r.L×r.W is now broken
?
Now you can see that all the problems above may be solved by merily coding what you need, say, with the following approach:
// use "unexported" fields
type room struct {
l int
w int
a int
}
func (r *room) SetL(v int) {
r.l = v
updateArea()
}
func (r *room) SetW(v int) {
r.w = v
updateArea()
}
func (r *room) GetA() int {
return r.a
}
func (r *room) updateArea() {
r.a = r.l * r.w
}
With this approach, you may be crystal-clear about all the issues above.
Remember that the programs are written for humans to read and only then for machines to execute; it's paramount for proper software engeneering to keep the code as much without any magic or intricate hidden dependencies between various parts of of it as possible. Please remember that
Software engineering is what happens to programming
when you add time and other programmers.
© Russ Cox
See more.

Go: How to use named arguments when calling a function?

How can I invoke a function while using named arguments?
(If it's unclear what named arguments are, here's an example of using them in Python)
Example of what I'd like to do:
func Add(a int, b int) int {
return a + b
}
func main() {
c := Add(a: 1, b:3)
return c
}
However, the above gives me the error:
unexpected :, expecting comma or )
(it's referring to the ':' right after the 'a')
In brief: the Go language does not support named args but IDEs do (see below).
I agree that named arguments could be useful in Go. It could help avoid bugs. For example, just yesterday my tests picked up a bug where the source and dest. parameters to copy() (Go built-in function) were back to front.
However, there are probably hundreds of useful language features that Go could have. We try to avoid adding non-essential features to the language to keep things simple. Once you've used Go for a large project you will greatly appreciate how much simpler things are compared to other languages. (I have used more than a dozen languages professionally and Go is by far the least annoying.)
But actually you can have named arguments if your IDE supports it. For example, I use GoLand and when you enter the arguments to a function it shows the parameter name (in light gray) in-line with a colon before the value. This is even better than what you are used to as you don't even have to type the name!
Go does not have named arguments. The closest thing I know of in Go to named arguments is using a struct as input. So for your example you could do -
type Input struct {
A int
B int
}
func Add(in Input) int {
return in.A + in.B
}
func main() {
c := Add(Input{A: 1, B: 3})
return c
}

confusion in understanding type conversions in go

package main
import (
"fmt"
)
type val []byte
func main() {
var a []byte = []byte{0x01,0x02}
var b val = a
fmt.Println(a)
fmt.Println(b)
}
o/p:
[1 2]
[1 2]
Here, my understanding is that a,b identifier share the same underlying type([]byte). so we can exchange the values b/w 2 variables.
package main
import (
"fmt"
)
type abc string
func main() {
fm := fmt.Println
var second = "whowww"
var third abc = second //compile Error at this line 12
fm(second)
fm(third)
}
In line 12 I'm not able to assign the variable.
This Error can be eliminated by using Explicit conversion T(x), I want to understand why we cannot do implicit conversion
As both variables share the same underlying-type, but I'm not able to assign it.
can someone explain the reason behind these?
IF possible provide me the good documentation for type conversions between variables, struct types, function parameters.
This is by design. The Go programming language requires assignment between different types to have an explicit conversion.
It might look like you're simply aliasing the string type to have a different name, but you're technically creating a new type, with a storage type of string, there's a subtle difference.
The way you would define an alias in Go (as of 1.9) is subtly different, theres an equals sign.
type abc = string
If there's any confusion as to why Go doesn't have implicit conversions, it might seem silly when you're only dealing with an underlying string type, but with more complex types, it ensures that the programmer knows just by looking at the code that a conversion is happening.
Its especially helpful in debugging an application, particularly when converting between numeric types to know when a conversion is taking place, so that if there is a truncation of bits i.e. uint64 to uint32, it is obvious to see where that is happening.
https://tour.golang.org/basics/13

Why is unsafe.Sizeof considered unsafe?

Consider the following:
import (
"log"
"unsafe"
)
type Foo struct {
Bar int32
}
func main() {
log.Println(int(unsafe.Sizeof(Foo{})))
}
Why is determining the size of a variable considered unsafe, and a part of the unsafe package? I don't understand why obtaining the size of any type is an unsafe operation, or what mechanism go uses to determine its size that necessitates this.
I would also love to know if there are any alternatives to the unsafe package for determining size of a known struct.
Because in Go if you need to call sizeof, it generally means you're manipulating memory directly, and you should never need to do that.
If you come from the C world, you'll probably most often have used sizeof together with malloc to create a variable-length array - but this should not be needed in Go, where you can simply make([]Foo, 10). In Go, the amount of memory to be allocated is taken care of by the runtime.
You should not be afraid of calling unsafe.Sizeof where it really makes sense - but you should ask yourself whether you actually need it.
Even if you're using it for, say, writing a binary format, it's generally a good idea to calculate by yourself the number of bytes you need, or if anything generate it dynamically using reflect:
calling unsafe.Sizeof on a struct will also include the number of bytes added in for padding.
calling it on dynamically-sized structures (ie. slices, strings) will yield the length of their headers - you should call len() instead.
Using unsafe on a uintptr, int or uint to determine whether you're running on 32-bit or 64-bit? You can generally avoid that by specifying int64 where you actually need to support numbers bigger than 2^31. Or, if you really need to detect that, you have many other options, such as build tags or something like this:
package main
import (
"fmt"
)
const is32bit = ^uint(0) == (1 << 32) - 1
func main() {
fmt.Println(is32bit)
}
From the looks of the unsafe package the methods don't use go's type safety for their operations.
https://godoc.org/unsafe
Package unsafe contains operations that step around the type safety of
Go programs.
Packages that import unsafe may be non-portable and are not protected
by the Go 1 compatibility guidelines.
So from the sounds of it the unsafe-ness is in the kind of code being provided, not necessarily from calling it in particular
Go is a type safe programming language. It won't let you do stuff like this:
package main
type Foo = struct{ A string }
type Bar = struct{ B int }
func main() {
var foo = &Foo{A: "Foo"}
var bar = foo.(*Bar) // invalid operation!
var bar2, ok = foo.(*Bar) // invalid operation!
}
Even if you use the type assertion with the special form that yields an additional boolean value; the compiler goes: haha, nope.
In a programming language like C though, the default is to assume that you are in charge. The program below will compile just fine.
typedef struct foo {
const char* a_;
} foo;
typedef struct bar {
int b_;
} bar;
int main() {
foo f;
f.a_ = "Foo";
bar* b = &f; // warning: incompatible pointer types
bar* b2 = (bar*)&f;
return 0;
}
You get warnings for things that are probably wrong because people have learned over time that this is a common mistake but it's not stopping you. It's just emitting a warning.
Type safety just means that you can't make the same mistake C programmers have made a thousand times over already but it is neither unsafe nor wrong to use the the unsafe package or the C programming language. The unsafe package has just been named in opposition to type safety and it is precisely the right tool when you need to fiddle with the bits (manipulate the representation of things in memory; directly).

Store a collection of constructors for types that all conform to the same interface

I'm making an app that'll need sets of rules to run a job. The app offers the possibility to express the rules in one of several different languages. I therefore have defined an interface to a live rules engine, that offers the methods that the app will need to query the current set of rules. Behind this interface, there will be one different type of engine, according to the source language.
Now I'd like to instantiate a rules engine according to the rule file's extension. But I get some errors which I have a hard time to overcome.
Let me first offer this simplified skeleton :
package main
//
//
// The interface
type RulesEngine interface {
SomeRuleEvaluator(string) bool
}
//
//
// An implementation, with its constructor
type ASimpleRulesEngine struct {
// I've also tried with :
// RulesEngine
}
func NewASimpleRulesEngine(context string) *ASimpleRulesEngine {
re := ASimpleRulesEngine{}
return &re
}
func (re *ASimpleRulesEngine) SomeRuleEvaluator(dummy string) bool {
return true
}
//
//
// A client, that'll want to be able to choose a constructor
var rulesEngineConstructorsPerExtension map[string](func(string) RulesEngine)
func init() {
rulesEngineConstructorsPerExtension = make(map[string](func(string)RulesEngine))
rulesEngineConstructorsPerExtension[".ini"] = NewASimpleRulesEngine
}
func main() {
}
When I try to build this, I get 35: cannot use NewASimpleRulesEngine (type func(string) *ASimpleRulesEngine) as type func(string) RulesEngine in assignment
I've also tried :
assigning without a pointer, although I felt stupid while trying it
having an intermediate step in the initfunction, where I'd create a new(func (string) RulesEngine) and then assign to it, with and without pointer.
storing function pointers like in C, but the compiler said it could not take the adress of my function.
I'm not that familiar with Go and this felt a bit surprising. What would be the proper type signature to use ? Is this possible at all ? If it's unavoidable, I'll obviously have a simple array of extensions on one side (to check if a file is potentially a rules file), and a big switch on the other side to provide the adequate constructor, but as much possible I'd like to avoid such duplication.
Thank you for any insight !
(edited : I've accepted my own answer for lack of any other, but its most relevant part is #seh 's comment below)
Following #JorgeMarey 's comment, and not wanting to sacrifice the constructor's type signature, I came up with this. But it does feel very tacky to me. I'll be glad to hear about a cleaner way.
func init() {
rulesEngineConstructorsPerExtension = make(map[string](func(string)RulesEngine))
cast_NewASimpleRulesEngine := func(content string) RulesEngine {
return NewASimpleRulesEngine(content)
}
rulesEngineConstructorsPerExtension[".ini"] = cast_NewASimpleRulesEngine
}
(an attempt to explicitly cast with (func(string)RulesEngine)( NewASimpleRulesEngine) was deemed unfit by the compiler too)

Resources