What types are expected from stacked dot notation? - ats

Consider:
#include "share/atspre_staload.hats"
fun plus_int_int(x: int, y: int): int = x + y
symintr .plus
overload .plus with plus_int_int
implement main0() =
println!("4+4+4 = ", ((4).plus(4)).plus(4))
This works, and it's clear that it can't be 4.plus() as the 4. is taken as a float. What I'd like to know is, why doesn't (4).plus(4).plus(4) work? ATS finds no valid overload for the second .plus, which tells me that it's expecting some other type than (int, int) -> int. What type is it expecting?

The current ATS2 parser interprets
(4).plus(4).plus(4)
as
((4).plus)((4).plus)(4)
I cannot easily fix the parser. I will think about it when implementing ATS3.

Related

When is the tilde not necessary in Go generics?

With Golangs new generics we have the tilde operator ~ which will match the underlying type. In what case is it valid to NOT match the underlying type? I'm trying to understand why the current behavior with the tilde is not the default behavior. It seems unnecessary to support both.
For example, why would you write
interface { int }
and not
interface { ~int }
What benefit to you would it be to write a method that is so strict that it could not accept something like
type MyInt int
Why is the tilde behavior not the default, and thus the language would not require another operator?
Not using the ~ operator means you only accept the listed exact types. Why should this matter?
You may want to use the values of the exact types to set to other variables and else type conversion would be required. And because the saying goes "new type, new method set". New types having the same underlying type have their own method sets.
You may want the "original" behavior of the value, which may change if it has a different method set.
For example, let's say you want to print the number like this:
type Num interface{ ~int }
func foo[T Num](v T) {
fmt.Println(v)
}
If MyInt has a String() string method:
type MyInt int
func (m MyInt) String() string { return "bar" }
The output might not be what foo() would want, because the fmt package checks if a printed value has a String() string method, and if so, it is called to acquire its string representation:
foo(1)
foo(MyInt(1))
This will output (try it on the Go Playground):
1
bar
If you only allow int:
type Num interface{ int }
You can still call foo() with a value of type MyInt, using a type conversion:
foo(1)
x := MyInt(1)
foo(int(x))
And output will be what foo() wants, not what MyInt would want (try this one on the Go Playground):
1
1
Yes, this would also be possible if foo() itself would do the conversion, but this clearly documents you want a pure int, with int's behavior, and not something that is an int with a different, custom behavior.
Why is the tilde behavior not the default
Because it would be confusing and semantically unsound to write a function like func Foo[T int](v T) that accepts type parameters that are not int. Then the meaning of int in interface constraints would not be the same as everywhere else. (More on this discussion)
What benefit to you would it be to write a method that is so strict [...]
Indeed if the constraint includes only one exact type, using type parameters is moot. If the type set of the constraint has cardinality 1, you should just remove the type parameter.
A function like:
func Foo[T int](v T)
can only ever be instantiated with exactly int, so it can (and should!) be simply written with regular arguments:
func Foo(v int)
When the type set cardinality is N, which includes single tilde types, but also unions, makes it basically impossible to write exhaustive type switch, since using ~ in case statements is not allowed (yet?):
func Foo[T ~int | ~string](v T) {
switch t := any(v).(type) {
case int: // ok
case string: // ok
// how to match other possible types then?
}
}
In this particular case, an exhaustive type switch can be written only if the constraint includes exact types:
func Foo[T int | string](v T) {
switch t := any(v).(type) {
case int: // ok
case string: // ok
default:
panic("should not occur")
}
}
This should not arise frequently in practice: if you find yourself switching on the type parameter, you should ask yourself if the function really needs to be generic. However the use case is relevant when designing your code.
Why is the tilde behavior not the default, and thus the language would not require another operator?
Because if the approximation would be the default unconditionally you could not express the fact that your polymorphic function requires an int and not a MyInt. You would then have to introduce an operator like strict and write %int. Nothing gained.

Why is a float64 type number throwing int related error in Go

I am trying to grasp Golang, in one of the tutorial example it says that An untyped constant takes the type needed by its context.
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
// Shift it right again 99 places, so we end up with 1<<1, or 2.
Small = Big >> 99
)
func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 {
return x * 0.1
}
func main() {
fmt.Println(needInt(Small))
fmt.Println(needFloat(Small))
// Here Big is too large of a number but can be handled as a float64.
// No compilation error is thrown here.
fmt.Println(needFloat(Big))
// The below line throws the following compilation error
// constant 1267650600228229401496703205376 overflows int
fmt.Println(Big)
}
When calling fmt.Println(Big) why is Golang treating Big as an int where as by context it should be float64?
What am I missing?
What is the context for fmt.Println? In other words, what does fmt.Println expect Big to be? An interface{}.
From the Go Blog on Constants:
What happens when fmt.Printf is called with an untyped constant is that an interface value is created to pass as an argument, and the concrete type stored for that argument is the default type of the constant.
So the default type of the constant must be an int. The page goes on to talk about how the defaults get determined based on syntax, not necessarily the value of the const.
Big in fmt.Println(Big) has type integer which is more than max int value 9223372036854775807
you can find max int from this logic
const MaxUint = ^uint(0)
const MaxInt = int(MaxUint >> 1)
fmt.Println(MaxInt) // print 922337
2036854775807
To fix it, you need to cast it to float64 like this
fmt.Println(float64(Big))

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.

Any one can make sense of connStateInterface?

func (c *conn) setState(nc net.Conn, state ConnState) {
...
c.curState.Store(connStateInterface[state])
...
}
// connStateInterface is an array of the interface{} versions of
// ConnState values, so we can use them in atomic.Values later without
// paying the cost of shoving their integers in an interface{}.
var connStateInterface = [...]interface{}{
StateNew: StateNew,
StateActive: StateActive,
StateIdle: StateIdle,
StateHijacked: StateHijacked,
StateClosed: StateClosed,
}
I can't figure out the trick with connStateInterface, how exactly does it work?
There's a few things going on here...
The [...] declaration creates an actual array instead of a slice, so that indirection is removed. What's being declared here is an array of interface{} types... so you might wonder why the weird map-looking notation?
The StateXXX variables are simply constants declared further above, so they are ints... so the declaration is actually of the form index: value.
Here's a less obfuscated example of that using an array of ints:
var i = [...]int{4: 2, 2: 7}
This will allocate an array containing:
[0, 0, 7, 0, 2]
... note that index 2 has 7, index 4 has 2. Not a common way of declaring an array, but it's valid Go.
So going back to the original declaration, just take the example I gave above, and instead of int, make the array of type interface{}:
var i = [...]interface{}{4: 2, 2: 7}
And you'll get a similar array, but with nil interface values in place of zeroes.
Getting even closer to the original code, the StateXXX constants are just ints, only not literals like in my example.
So, what's the point of all this? Why all the obfuscation?
It's a performance hack. The function c.curState.Store() takes an argument of type interface{}. If you were to pass it an int, the compiled code would have to fumble about with converting the type on each call. A more clear (though obviously impractical) illustration of this might be:
var val interface{}
for i := 0; i < 1000000; i++ {
// the types are different, compiler has to fumble int vs. interface{}
val = i
// do something with val
}
Every time you do val = i a conversion between int and interface{} needs to happen. The code you posted avoids this by creating a static lookup table where all the values are already of type interface.
Therefore, this:
c.curState.Store(connStateInterface[state])
is more efficient than this:
c.curState.Store(state)
Since state would, in this case, need to undergo the int -> interface{} conversion. In the optimized code, state is merely an index looking up a value into an array, the result of which gets you an interface{}... so the int -> interface{} type conversion is avoided.
I'm not familiar with that code, but I'd imagine it's in a critical path and the nanoseconds or whatever savings shaved off likely makes a difference.

How do I make an Interface that just allows integers?

I want to make a function that can take any int-like variable.
For example:
type IntegerType interface {
int
}
func MyFunc(arg IntegerType) {
fmt.Println(arg)
}
This won't compile because I can't base interface inheritance on a non-interface type, but that's exactly what I want to do!
The end goal is this:
type Token int
var t Token
t = 3
MyFunc(t)
How do I guarantee that my argument is an integer, while still allowing integer aliases?
How do I guarantee that my argument is an integer, while still allowing integer aliases?
You rely on type conversion.
func MyFunc(arg int) {
fmt.Println(arg)
}
type Token int
var t Token
t = 3
MyFunc(int(t))
s := "This is a string, obviously"
MyFunc(int(s)) // throws a compiler error
There's really no need to define an interface for just the type int because you can rely on the languages type conversion behavior to enforce compile time type safety in these types of scenarios.
One thing to note is this is slightly more accepting than an int only interface would be. For example, an int64 will convert to a int, even a float will using typical rounding conventions. Invalid conversions will produce the following error; cannot convert s (type string) to type int. From what I can tell only native numeric types or aliases for them will convert.

Resources