What does it mean for a constant to be untyped? - go

The Go Programming Language Specification says that:
Constants may be typed or untyped
I am having a little doubt in my understanding.
Consider this example in the spec:
const l = "hi" // l == "hi" (untyped string constant)
const m = string(k) // m == "x" (type string)
The spec says:
constant may be given a type explicitly by a constant declaration or
conversion, or implicitly when used in a variable declaration or an
assignment or as an operand in an expression
By this statement, why isn't l typed since it is clearly a constant declaration?
This behaviour is clearer with another example
type Foo string
func f(a Foo) {}
func main() {
f("sarkozy")
const t = "julie gayet"
f(t)
s := "hollande"
//compile error
// f(s)
f(Foo(s)) // ok
}
Is the reason that f("sarkozy") compiles be due to this statement on Assignability in the spec?
x is an untyped constant representable by a value of type T.
My argument is the following:
"sarkozy" a an untyped literal.
Thus "sarkozy" being representable by Foo means I can type coerce like this Foo("sarkozy")
f(s) fails because s is not untyped.

Why isn't l typed since it is clearly a constant declaration?
Yes, it is clearly a constant declaration, as your quote says:
constant may be given a type explicitly by a constant declaration
However, in your case there is no explicitly given type. You can only have an implicitly given type when "used in a variable declaration or an assignment or as an operand in an expression".
Is the reason that f("sarkozy") compiles be due to this statement on Assignability in the spec?
Yes, the reason that f("sarkozy") compiles is because the untyped constant of "sarkozy" has a type given implicitly when used as an operand in an expression such as in your case.
"sarkozy" is implicitly given the type of Foo
So why doesn't f(s) compile? (okay, that was not in the question, but the question remains)
Your argument states that: "f(s) fails because s is not untyped."
True that s is not untyped. s is a variable and not a constant, and variables cannot be untyped.
The Go specs states for Variable Declarations:
If the type is absent and the corresponding expression evaluates to an untyped constant, the type of the declared variable is as described in §Assignments.
And that refers, from what I understand to the following:
the constant is first converted to type bool, rune, int, float64, complex128 or string respectively, depending on whether the value is a boolean, rune, integer, floating-point, complex, or string constant.
So, the following line:
s := "hollande"
will declare the variable (not constant) s of type string because the right-hand expression is an untyped string constant. The type is implicitly given during the declaration of the variable, not by analyzing what context it which it later on will be used.
f(s) will then result in a compile error because you try to use a value of type string where a value of type Foo is expected.

Related

Implicit type conversion constant vs variables

I have faced a situation where I have some integer value in a constant and multiplying it with math.Pi constant as below:
func main() {
const a = 5
fmt.Printf("%v", a*math.Pi)
}
On execution it gives following result:
15.707963267948966
But, when I change the constant to variable (variable a) as below:
func main() {
a := 5
fmt.Printf("%v", a*math.Pi)
}
On compilation it gives the following error:
constant 3.14159 truncated to integer
As far as I understand implicit numeric type conversion is working when all operands of expression are constants, but not working when any of these a variable.
But why is this happening?
It's happening because of Go's untyped constants. In both cases, you are not explicitly specifying a type.
In the first case, you are defining an untyped constant (you could also define a typed constant by using const a float64 = 5). For an untyped constant, a type will only be inferred when it’s used in a context that requires a type – i.e. when you are multiplying it with math.Pi, the compiler will "guess" that you want to have a float there, and everything works fine.
In the second case, a variable of course has to have a type, so the type inference takes place when declaring the variable, and since you used "5", the compiler will "infer" int, and multiplying an int and a float is not possible in Go. You could use e.g. a:=5.0 or var a float64 = 5 to declare a as a float64, then this code would work as well.
See this blog post for more details.

Why does %T not print the type of my constant?

I'm just learning golang using the official tour/tutorial. In one of the examples, I see a note that says An untyped constant takes the type needed by its context.
I'm trying this:
package main
import "fmt"
const (
// Create a huge number by shifting a 1 bit left 100 places.
// In other words, the binary number that is 1 followed by 100 zeroes.
Big = 1 << 100
)
func main() {
fmt.Printf("Big is of type %T\n", Big)
}
But this fails when I run it, with:
# command-line-arguments
./compile63.go:12:13: constant 1267650600228229401496703205376 overflows int
Why am I unable to discover the type of the constant this way? (Please note I'm a total noob and quite possibly haven't yet discovered enough about the language to be able to solve this myself).
func Printf
func Printf(format string, a ...interface{}) (n int, err error)
Printf formats according to a format specifier and writes to standard
output. It returns the number of bytes written and any write error
encountered.
The Go Programming Language
Specification
Variable
declarations
A variable declaration creates one or more variables, binds
corresponding identifiers to them, and gives each a type and an
initial value.
If a list of expressions is given, the variables are initialized with
the expressions following the rules for assignments. Otherwise, each
variable is initialized to its zero value.
If a type is present, each variable is given that type. Otherwise,
each variable is given the type of the corresponding initialization
value in the assignment. If that value is an untyped constant, it is
first converted to its default type; if it is an untyped boolean
value, it is first converted to type bool. The predeclared value nil
cannot be used to initialize a variable with no explicit type.
Constants
Constants may be typed or untyped. Literal constants, true, false,
iota, and certain constant expressions containing only untyped
constant operands are untyped.
A constant may be given a type explicitly by a constant declaration or
conversion, or implicitly when used in a variable declaration or an
assignment or as an operand in an expression. It is an error if the
constant value cannot be represented as a value of the respective
type.
An untyped constant has a default type which is the type to which the
constant is implicitly converted in contexts where a typed value is
required, for instance, in a short variable declaration such as i := 0
where there is no explicit type. The default type of an untyped
constant is bool, rune, int, float64, complex128 or string
respectively, depending on whether it is a boolean, rune, integer,
floating-point, complex, or string constant.
Numeric types
A numeric type represents sets of integer or floating-point values.
Some predeclared architecture-independent numeric types:
int32 the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64 the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)
The value of an n-bit integer is n bits wide and represented using
two's complement arithmetic.
There are also some predeclared numeric types with
implementation-specific sizes:
uint either 32 or 64 bits
int same size as uint
Big is an untyped constant. There is no type to discover. It is given a type when used in a variable or assignment. The default type for the untyped constant Big is int.
const Big = 1 << 100
In Go, all arguments are passed by value as if by assignment. For fmt.Printf, the second argument is of type interface{}. Therefore, equivalently,
var arg2 interface{} = Big // constant 1267650600228229401496703205376 overflows int
fmt.Printf("Big is of type %T\n", arg2)
The default type for an untyped integer constant is int. Overflow is a compile-time error.
For example,
package main
import "fmt"
const (
// Create a huge number by shifting a 1 bit left 100 places.
// In other words, the binary number that is 1 followed by 100 zeroes.
Big = 1 << 100
)
func main() {
var arg2 interface{} = Big
fmt.Printf("Big is of type %T\n", arg2)
fmt.Printf("Big is of type %T\n", Big)
}
Playground: https://play.golang.org/p/9tynPTek3wN
Output:
prog.go:12:6: constant 1267650600228229401496703205376 overflows int
prog.go:15:13: constant 1267650600228229401496703205376 overflows int
Reference: The Go Blog: Constants
While it is true that an untyped constant takes the type needed by its context, the type it can assume is limited by the primitives of the language, so a constant that big is not actually usable anywhere in the code, due to the fact that it does not fit even in an uint64. The only use it could have would be that of using it in another constant expression, because otherwise that error will always be thrown.
Note that in Printf (and similar functions), the constant is converted to an interface{}, and thus it takes a type of int by default. For 32-bit machines, you need to do a type conversion first if you have a constant expression which overflows int32.
const i = 1 << 50
fmt.Println(i) // => constant 1125899906842624 overflows int
fmt.Println(int64(i)) // => 1125899906842624
If you want to do proper arithmetic on arbitrarily big numbers, there is a handy package: math/big.
i := big.NewInt(1)
i.Lsh(i, 100)
fmt.Println(i.String())
Playground

Golang not able to detect implicitly typed const when assigning to a variable

When I try to assign an implicitly typed constant to a variable, the assigned variable doesn't detect the custom type and instead gets assigned as the underlying primitive type. i.e;
For:
type Custom string
const (
First Custom = "10"
Second = "20"
)
If I have a function:
func SomeFunc( x Custom) {
fmt.Printf("Inside func %v %v", x, reflect.TypeOf(x))
}
Then when I:
out := Second
SomeFunc(out)
it errors with:
cannot use out (type string) as type Custom in argument to SomeFunc
However SomeFunc(Second) works fine.
Also
fmt.Printf("%v %v\n",reflect.TypeOf(second),reflect.TypeOf(out)) //prints string string
Here is the reproducer: https://play.golang.org/p/Iv-C1ee992
Can someone help me understand what is happening here?
Second is an untyped const and has this property (https://blog.golang.org/constants):
An untyped constant is just a value, one not yet given a defined type
that would force it to obey the strict rules that prevent combining
differently typed values.
...
Assigning them to a variable of any type compatible with strings works
without error.
On the contrary out is a variable of type string. Again from the blog post:
and by now you might be asking, "if the constant is untyped, how does
str get a type in this variable declaration?" The answer is that an
untyped constant has a default type, an implicit type that it
transfers to a value if a type is needed where none is provided. For
untyped string constants, that default type is obviously string

Custom type passed to function as a parameter

When I define a custom type, it seems that the type of the underlying type makes a difference about whether I can pass it to a function as is or I need to convert it.
Question is:
Why does RuneFunc and StringMap work, but not Integer?
https://play.golang.org/p/buKNkrg5y-
package main
type RuneFunc func(rune) rune
type Integer int
type StringMap map[string]string
func main() {
//m := make(StringMap)
//mf(m)
var i Integer = 5
nf(i)
//var f func(rune) rune
//ff(f)
}
func mf(i map[string]string) {
}
func ff(i func(rune)rune) {
}
func nf(i int) {
}
Here, when I run this function called nf with Integer it complains although int is the underlying type. But when I call mf or ff they run successfully.
Integer and int
int and your new type Integer are 2 different, distinct types. Where Integer is expected, you have to pass a value of type Integer.
If you have an Integer value, you may use a simple type conversion to make it a value of type int, because the underlying type of Integer is int:
var i Integer = 5
nf(int(i))
What may be confusing and interesting at the same time is that you are allowed to pass an untyped constant without conversion:
nf(5)
Try these on the Go Playground.
The reason for this is in the Spec: Assignability:
A value x is assignable to a variable of type T ("x is assignable to T") in any of these cases:
[...]
x is an untyped constant representable by a value of type T.
5 is an untyped constant which is representable by a value of type int because the untyped constant 5 has a default type int, so it is representable by a value of type Integer (which has the same default type).
If you check the other assignability rules (not included in above quotation), none of them match the case where you attempt to pass a value of Integer for the parameter of type int, so that's not allowed.
See related question: Golang: Creating a Constant Type and Restricting the Type's Values
RuneFunc and func(rune) rune
The difference between this case and the previous one (Integer and int) is that int is a named type and func(rune) rune is not.
And there's an assignability rule which allows this:
x's type V and T have identical underlying types and at least one of V or T is not a named type.
So in this case:
var f RuneFunc
ff(f)
f is a named type, but the parameter type of ff() is func(rune) rune which is unnamed, so the assignment is allowed.
Go has a strict type system. Just because your type is merely an alias for int doesn't mean you can interchange the two freely, you'll still have to do a type conversion. Below is a working version of your main, here's the code on play ground: https://play.golang.org/p/BDTXnnG9Lg

what's wrong with golang constant overflows uint64

userid := 12345
did := (userid & ^(0xFFFF << 48))
when compiling this code, I got:
./xxxx.go:511: constant -18446462598732840961 overflows int
Do you know what is the matter with this and how to solve it ?
Thanks.
^(0xFFFF << 48) is an untyped constant, which in go is an arbitrarily large value.
0xffff << 48 is 0xffff000000000000. When you negate it, you get -0xffff000000000001 (since with two's complement, -x = ^x + 1, or ^x = -(x + 1)).
When you write userid := 12345, userid gets the type int. Then when you try to and (&) it with the untyped constant -0xffff000000000001 the compiler figures that this constant needs to be an int. At this point, the compiler complains because the value is too large in magnitude to be an int.
If you're trying to get the constant 0x0000ffffffffffff, then you can use 1<<48 - 1, which (if you've got 64-bit ints), will fit. Since your code will never work if int is 32-bits, then you should probably use int64 in your code rather than int to make it portable.
The blog post https://blog.golang.org/constants explains how constants work, and some background on why they are the way they are.
The Go Programming Language Specification
Constants
Numeric constants represent values of arbitrary precision and do not
overflow.
Constants may be typed or untyped.
A constant may be given a type explicitly by a constant declaration or
conversion, or implicitly when used in a variable declaration or an
assignment or as an operand in an expression. It is an error if the
constant value cannot be represented as a value of the respective
type.
An untyped constant has a default type which is the type to which the
constant is implicitly converted in contexts where a typed value is
required, for instance, in a short variable declaration such as i := 0
where there is no explicit type. The default type of an untyped
constant is bool, rune, int, float64, complex128 or string
respectively, depending on whether it is a boolean, rune, integer,
floating-point, complex, or string constant.
Numeric types
int is an implementation-specific size, either 32 or 64 bits.
userid is of type int. For example,
package main
import "fmt"
func main() {
userid := 12345
did := uint64(userid) & ^uint64(0xFFFF<<48)
fmt.Println(userid, did)
}
Output:
12345 12345

Resources