Re: [go-nuts] Re: An mistake in tip spec?

2022-01-07 Thread 'Robert Griesemer' via golang-nuts
Indeed. There's no shortcuts possible here. Hopefully this works better
(and closely matches the implementation):
https://go-review.googlesource.com/c/go/+/376834
- gri

On Thu, Jan 6, 2022 at 10:08 PM Axel Wagner 
wrote:

> On Fri, Jan 7, 2022 at 2:35 AM 'gri' via golang-nuts <
> golang-nuts@googlegroups.com> wrote:
>
>> Thanks for raising this issue. This is clearly a bug in the spec, or at
>> the very least "an imprecision". Hopefully this is better:
>> https://go-review.googlesource.com/c/go/+/375799 .
>
>
> ISTM this still fails for `int | any`, which according to the rules has
> specific types `int`, but should have no specific types.
>
>
>>
>> The set of specific types is an approximation for the actual type set. In
>> practice we only need to consider those explicitly mentioned types. If the
>> type set is infinite, we can't do much (with respect to operations). To
>> make that simple elsewhere in the spec we want the set of specific types be
>> empty in that case. An alternative approach might be to say the set of
>> specific types is infinite in that case and exclude such infinite sets
>> where we look at specific types.
>>
>> - gri
>>
>> On Thursday, January 6, 2022 at 1:23:31 PM UTC-8
>> axel.wa...@googlemail.com wrote:
>>
>>> The more I think about it, the more I'm inclined to agree that there is
>>> something wrong with the spec (and maybe the idea of sets of specific types
>>> in general).
>>> Because what's the set of specific types of `interface { int | any }`?
>>> From the definition, it should be `int` - the union with an empty set is a
>>> no-op. But the *type set* of that is all types. Which means that this
>>> should be allowed, as per the spec, which is obviously nonsense:
>>>
>>> type C interface {
>>> int | any
>>> }
>>>
>>> func F[T C](v T) {
>>> fmt.Println(int(v))
>>> }
>>>
>>> So, logically, the type set of an interface type without type elements
>>> should really be the set of all types (or rather "the set of all underlying
>>> types"), not the empty set, for the algorithm to make sense. In that case,
>>> the specific types of `int | any` are the set of all types and the specific
>>> types of `int ; any` is `int`.
>>>
>>> I'm not sure if this would be sound, though, as we now get back to the
>>> situation of reducing back from the set of all types to some subset. Maybe
>>> the new restrictions put in place (namely that interfaces with methods
>>> can't appear in unions) are enough to make this easy to manage. I don't
>>> know. Thinking through that would take time.
>>>
>>> On Thu, Jan 6, 2022 at 8:37 PM Ian Lance Taylor 
>>> wrote:
>>>
 On Thu, Jan 6, 2022 at 11:32 AM Axel Wagner
  wrote:
 >
 > On Thu, Jan 6, 2022 at 8:18 PM Ian Lance Taylor 
 wrote:
 >>
 >> On Thu, Jan 6, 2022 at 8:59 AM 'Axel Wagner' via golang-nuts
 >>  wrote:
 >> >
 >> > • From the definition of type elements, we can see that an
 embedded interface type is a type element. Therefore, `any` is a type
 element.
 >> > • `any` is an alias for `interface{}`, therefore it is a type
 without any type elements, therefore the set of its specific types is empty
 ("For an interface with no type elements, 푆 is the empty set.").
 >> > • `interface{ int; any }` is a type with two type elements. "For
 an interface with type elements, 푆 is the intersection of the specific
 types of its type elements.". Intersecting with an empty set (the specific
 types of `any`) gives an empty set
 >> > • Therefore, the set of specific types of `interface{ int; any }`
 is the empty set.
 >>
 >> That doesn't seem right.  "any" is an interface type and writing
 >> "interface { any }" just embeds the empty interface, which has no
 >> effect.
 >
 >
 > I think it has no effect on the type set. But it has an effect on the
 set of specific types.
 >
 > I believe both the rule "for an interface type without type elements
 S is empty" and the rule "for an interface type with type elements, S is
 the intersection of the specific types of its type elements" are vital to
 the working of S - they are, what makes the set easily computable.
 >
 > To answer the question from your comment in the CL: I believe the
 grammar is very unambiguous about `any` being a type element:
 >
 >> InterfaceType = "interface" "{" { InterfaceElem ";" } "}" .
 >> InterfaceElem = MethodElem | TypeElem .
 >> MethodElem = MethodName Signature .
 >> MethodName = identifier .
 >> TypeElem = TypeTerm { "|" TypeTerm } .
 >> TypeTerm = Type | UnderlyingType .
 >> UnderlyingType = "~" Type .
 >
 >
 > There is no signature, so it's not a MethodElem. Therefore it must be
 a TypeElem.

 Yeah, I think there is something wrong in the current spec.  It can't
 be the case that embedding an interface eliminates all specific types.

 Ian

>>> --
>> You received this 

[go-nuts] Generics and parentheses

2020-07-14 Thread 'Robert Griesemer' via golang-nuts
We have received a variety of feedback on the generics draft design

(blog ). Thanks to everyone who
took the time to read it, play with generics code in the playground
, file issues, and send us their thoughts.

Not unexpectedly, many people raised concerns about the syntax,
specifically the choice of parentheses for type parameter declarations and
generic type and function instantiations.

A typical computer keyboard provides four easily accessible pairs of
single-character symmetrical "brackets": parentheses ( and ), square
brackets [ and ], curly braces { and }, and angle brackets < and >. Go uses
curly braces to delineate code blocks, composite literals, and some
composite types, making it virtually impossible to use them for generics
without severe syntactic problems. Angle brackets require unbounded parser
look-ahead or type information in certain situations (see the end of this
e-mail for an example). This leaves us with parentheses and square
brackets. Unadorned square brackets cause ambiguities in type declarations
of arrays and slices, and to a lesser extent when parsing index
expressions. Thus, early on in the design, we settled on parentheses as
they seemed to provide a Go-like feel and appeared to have the fewest
problems.

As it turned out, to make parentheses work well and for
backward-compatibility, we had to introduce the type keyword in type
parameter lists. Eventually, we found additional parsing ambiguities in
parameter lists, composite literals, and embedded types which required more
parentheses to resolve them. Still, we decided to proceed with parentheses
in order to focus on the bigger design issues.

The time has come to revisit this early decision. If square brackets alone
are used to declare type parameters, the array declaration

type A [N]E

cannot be distinguished from the generic type declaration

type A[N] E

But if we are comfortable with the extra type keyword, the ambiguity
disappears:

type A[type N] E

(When we originally dismissed square brackets, the type keyword was not yet
on the table.)

Furthermore, the ambiguities that arise with parentheses appear not to
arise with square brackets. Here are some examples where extra parentheses
are not needed with square brackets:

using () using []

func f((T(int))  func f(T[int])

struct{ (T(int)) }   struct{ T[int] }

interface{ (T(int)) }interface{ T[int] }

[](T(int)){} []T[int]{}

To test this better understanding, and to get a feel for this alternative
notation, we will begin to make changes to our prototype implementation
such that it accepts either parentheses or square brackets (only one or the
other) in a generic Go package. Those changes will first appear as commits
to the dev.go2go branch
, and eventually in
the playground .

If square brackets don't lead to unforeseen issues, we have another fully
explored notation to choose from, which will allow us to make a more
informed decision.

- gri, iant

PS: For ambiguities with angle brackets consider the assignment

a, b = w < x, y > (z)

Without type information, it is impossible to decide whether the right-hand
side of the assignment is a pair of expressions

(w < x), (y > (z))

or whether it is a generic function invocation that returns two result
values

(w)(z)

In Go, type information is not available at compile time. For instance, in
this case, any of the identifiers may be declared in another file that has
not even been parsed yet.

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CAKy0tf4agu1nNdwc%3DeZQt-KpvrD8XcFm502x46tkriz3gEJUXg%40mail.gmail.com.


[go-nuts] Go Blog: Next steps toward Go 2

2019-06-27 Thread 'Robert Griesemer' via golang-nuts
For those of you who are not actively following the Go Blog
, yesterday we have posted a new entry on
proposals we are considering for Go 1.14:

https://blog.golang.org/go2-next-steps

- gri

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CAKy0tf56guU1vDDFEwfaH_E1M1JO0mzdmXyU8%2BTdV5hntTjGyQ%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.


Re: [go-nuts] Cyclic types

2018-05-09 Thread 'Robert Griesemer' via golang-nuts
PS: Here's an example where we (humans) can obviously compute the size of a
type, yet neither cmd/compile, gccgo, nor go/types have any success in
doing so:

type T struct {
a [10]int
b [len(T{}.a)]int
}

The problem is that all implementations have an "eager" (depth-first)
approach somewhere leading to requiring all of T to be set up before we can
determine the size of T.a. Specifically, when determining the size of T.b
we must be careful to not look at the size of T (which is in the process of
being determined), and only at the size of T.a (which we can obviously
tell). Furthermore, we must use an algorithm that computes the size of T.a
"on demand", not in the order as the fields appear (otherwise it wouldn't
work if b was before a). And so forth. All these things make size
computation more complicated and expensive. That question is: Is it worth
the extra cost? Or are these cases esoteric and don't show up in real code?
And if we use simpler algorithms, is there an easy way to describe which
types are accepted and which aren't?


On Wed, May 9, 2018 at 10:00 AM Robert Griesemer  wrote:

> This sounds all good.
>
> I am not disputing at all what you are saying, but a) the spec doesn't
> actually state any of this explicitly; and b) I agree that size computation
> is straight-forward once a type data structure is all constructed. The
> caveat is the 2nd part of this sentence: We're not doing always a correct
> job of setting up a type completely before it's used. Hence we have issues
> like #25305. The compiler does a better job than go/types most of the time;
> but sometimes it's the other way around.
>
> I think we also have been hesitant to simply disallow "cyclical types"
> (per your definition of cyclical) in the spec because we (or at least I)
> don't have a good understanding that our code actually detects exactly
> those. We have plenty of examples of code where we could determine the
> type's size but we still exclude the type. For instance
>
> type T = *T
>
> T has clearly the size of a pointer, yet we disallow (in the compiler)
> such types. In this case it's by design (of the type alias proposal), but
> it would be nice if we could relax it. But I'm not sure we (or I)
> understand all the consequences fully, quite yet. And I think we have other
> situations (not involving alias types) where we run into problems, even
> though we can compute the type's size.
>
> (FWIW, I don't think everybody equates "cyclic type" with "type size is
> not computable". People tend to use "cyclic" and "recursive"
> interchangeably for types. I was definitively using "cyclic" as "recursive"
> in #25305).
>
> More generally, I think it would be great if we could state exactly what
> you said in the spec:
>
> 1) Types for which their sizes cannot be computed (see 2) are invalid.
> 2) The size of a type is computable if ... (and then we give essentially
> the rules you outlined already).
>
> As said above, 2) requires all involved types to be set up sufficiently
> such that we can determine the relevant size information. Sometimes that's
> not the case. Hence my comment in the issue #25305.
>
> Finally, I agree that there shouldn't be a difference between cycle
> detection by a human and a computer. But the problem is that the computer
> may be using an algorithm that may be conservative, or incorrect, or not
> very general (for the sake of speed in the common case).
>
> On Wed, May 9, 2018 at 1:21 AM Jan Mercl <0xj...@gmail.com> wrote:
>
>> Robert Griesemer wrote in
>> https://github.com/golang/go/issues/25305#issuecomment-387624488 at May
>> 9th:
>>
>> I'm probably using incorrect assumptions. Let me summarize them here:
>>
>>
>> 1) A type is cyclical iff its size is not computable.
>>
>>
>> I'm really not sure if this is what the specification really means. If
>> not then I wonder why not, because
>>
>>
>> 2) Determining computability of the size of a type is trivial (wrt "we go
>> through great lengths to detect such cycles").
>>
>>
>> AFAICT, there are two classes of types.
>>
>>
>> In the first (scalar) class the size of T is a constant fully determined
>> by the kind of T: bool, integers, real and complex types, slices,
>> interfaces, pointers, maps, channels, functions. (The last three being just
>> a special case of a pointer.)
>>
>>
>> In the second (non-scalar) class a type T has size dependent
>> (transitively) on other types (T_1, ... T_n), possibly including T itself.
>> Scalar T_i brings no problem in computing the size of T.
>>
>>
>> For non-scalar T_i, all we need is a sentinel provided by knowing if the
>> size of a type is a) not yet determined, b) being determined, c)
>> determined/valid. When the size of T is needed, but not yet determined,
>> it's first marked as "being determined". If, during computation of the size
>> of T, we run into the sentinel, ie. we need to know the size of T_i marked
>> "size being determined", we have proved the size of T is not computable.
>>