proposal: spec: type inferred composite literals

Composite literals construct values for structs, arrays, slices, and maps. They consist of a type followed by a brace-bound list of elements. e.g.,
x := []string{"a", "b", "c"}

I propose adding untyped composite literals, which omit the type. Untyped composite literals are assignable to any composite type. They do not have a default type, and it is an error to use one as the right-hand-side of an assignment where the left-hand-side does not have an explicit type specified.
var x []string = {"a", "b", "c"}
var m map[string]int = {"a": 1}

type T struct {
  V int
}
var s []*T = {{0}, {1}, {2}}

a := {1, 2, 3} // error: left-hand-type has no type specified

Go already allows the elision of the type of a composite literal under certain circumstances. This proposal extends that permission to all occasions when the literal type can be derived.
This proposal allows more concise code. Succinctness is a double-edged sword; it may increase or decrease clarity. I believe that the benefits in well-written code outweigh the harm in poorly-written code. We cannot prevent poor programmers from producing unclear code, and should not hamper good programmers in an attempt to do so.
This proposal may slightly simplify the language by removing the rules on when composite literal types may be elided.
Examples
Functions with large parameter lists are frequently written to take a single struct parameter instead. Untyped composite literals allow this pattern without introducing a single-purpose type or repetition.
// Without untyped composite literals...
type FooArgs struct {
  A, B, C int
}
func Foo(args FooArgs) { ... }
Foo(FooArgs{A: 1, B: 2, C:3})

// ...or with.
func Foo(args struct {
  A, B, C int
}) { ... }
Foo({A: 1, B: 2, C: 3})

In general, untyped composite literals can serve as lightweight tuples in a variety of situations:
ch := make(chan struct{
  value string
  err   error
})
ch <- {value: "result"}

They also simplify code that returns a zero-valued struct and an error:
return time.Time{}, err
return {}, err // untyped composite literal

Code working with protocol buffers frequently constructs large, deeply nested composite literal values. These values frequently have types with long names dictated by the protobuf compiler. Eliding types will make code of this nature easier to write (and, arguably, read).
p.Options = append(p.Options, &foopb.Foo_FrotzOptions_Option{...}
p.Options = append(p.Options, {...}) // untyped composite literal

There is some prior art. We actually implemented this (or something very similar) in the lead-up to Go 1.
The spec changes:
https://codereview.appspot.com/5450067/
https://codereview.appspot.com/5449067/
The code changes:
https://codereview.appspot.com/5449071/
https://codereview.appspot.com/5449070/
https://codereview.appspot.com/5448089/
https://codereview.appspot.com/5448088/
There may be other changes that I'm missing. But in the end we backed them all out; it tended to make the code less readable on balance.

I only see one spec change there (the other one you linked is the compiler implementation).
At any rate: "tend[ing] to make less readable on balance" depends a lot on the specific code.  Presumably we've learned more about real-world Go usage (including Protocol Buffers and a variety of other nesting data-types) in the time since then - perhaps it's worth revisiting?
(I've badly wanted literals for return values and channel sends on many occasions - they would be particularly useful when a struct is just a named version of "a pair of X and Y" and the field names suffice to fully describe it.)

while I support more type eliding rules, I don't like the concept
of untyped composite literals. The new concept is too big for
Go 1, IMHO.
For example, composite literals for map and structs are different,
so I don't expect that you can { A: 1 } assign to a map[string]int.
And what will happen if the type doesn't have the A field? should
that be an error?
And, if we have untyped composite literal, we need to figure out
whether we allow const untyped composite literals.
BTW: I think every untyped type should have a default type,
otherwise it will be too confusing to use.

Under this proposal, the following assignments are identical:
var m map[string]int
m = map[string]int{A: 1}
m = {A: 1}
The only difference is that in the latter case, the type of the literal is derived from the RHS of the expression. In both cases, the compiler will interpret A as a variable name.
I would not allow const untyped composite literals; that's a can of worms.
I think untyped composite literals would be too confusing to use (and compile!) if they came with a default type. :)

On readability:
This would be a significant language change. Go code would become somewhat terser, on balance. In some cases this would lead to less readable code; in others more so.
I feel that the benefits would outweigh the costs, but that's obviously a subjective judgement. In particular, I think that lightweight tuples (as in the above examples) would be a substantial improvement in a number of places.

I assume that this proposal is simply expanding the places in which you can elide a type literal.
If so, referring to it as untyped composite literals is a bit confusing as untyped has a specific meaning in Go.
It might make more sense to consider each place separately. I don't see much of a point, other than consistency, in allowing
var t T = {}

since you could just do
var t = T{}

But the rest would certainly cut down on typing and allow nicer APIs in places.
For example,
Polygon({1, 2}, {3, 4}, {5, 4})

is arguably clearer than
Polygon([]image.Point{{1, 2}, {3, 4}, {5, 4}})

and the only alternative at present would be
Polygon(image.Point{1, 2}, image.Point{3, 4}, image.Point{5, 4})

I agree that the examples look nice, at a glance. But anything can look
nice without context.
To move this proposal forward, one should apply the change to a corpus of
real Go code so that we may observe its benefits and drawbacks in context.
On 7 October 2015 at 09:12, Damien Neil<EMAIL_ADDRESS>wrote:

On readability:
This would be a significant language change. Go code would become somewhat
terser, on balance. In some cases this would lead to less readable code; in
others more so.
I feel that the benefits would outweigh the costs, but that's obviously a
subjective judgement. In particular, I think that lightweight tuples (as in
the above examples) would be a substantial improvement in a number of
places.
—
Reply to this email directly or view it on GitHub
https://github.com/golang/go/issues/12854#issuecomment-146017331.

The examples look nice, but I'd suggest we don't introduce a
new kind of untyped literal. (I especially don't like allowing to
convert {A: 1} to map[string]int{ "A": 1 } implicitly.)
Instead, we can extend the spec by adding more places where
type can be elided. This has a higher chance being accepted.

To be clear, this proposal does not allow {A: 1} to implicitly become map[string]int{"A":1}.

https://github.com/golang/go/issues/12854#issuecomment-146016054
says these are allowed:
var m map[string]int
m = map[string]int{A: 1}
m = {A: 1}
Isn't the last one implicitly converts {A:1} to map[string]int{"A":1}?

In that last example, A is an identifier (i.e. for a string variable or constant) - not a string literal itself.

did you mean that the code is actually:
const A  = "A"
var m map[string]int
m = {A: 1}
Then there are more ambiguity in syntax.
const A = "A"
var T struct { A int }
m = {A: 1}
What does this mean?

x = {A: 1} is precisely equivalent to x = T{A: 1}, where T is the type of x.

But we currently don't accept
var m = map[string]int{A: 1}

We do, actually:
http://play.golang.org/p/YubepmdVwy
A := "A"
var m map[string]int
m = map[string]int{A: 1}
fmt.Println(m)

We're talking in circles.
In https://github.com/golang/go/issues/12854#issuecomment-146016054,
there is no mention of where does A come from.
Let me ask again, does this proposal support:
var m map[string]int
m = {A: 1}
var x struct { A int }
x = {A: 1}
where A is not otherwise defined?
The proposal says "Untyped composite literals are assignable to any
composite type."
so I'd assume the answer to my question is true.

If A is not otherwise defined, then the first case (m = {A: 1}) will fail to compile with the same error you would get if it were written m = map[string]int{A: 1}. i.e., it is syntactically valid but incorrect because A is undefined.

@minux The implementation of this proposal is actually rather straight-forward and the explanation reasonably simple and clear: Whenever the type of a composite literal is known, we can elide it. Once the type is known, the meaning of a composite literal key value ('A' in the previous examples) is answered the same as it is before.
(Implementation-wise this only affects the type-checker, and there's already provisions for this for the case where we allow type elision already.)
So the proposal is indeed simply a relaxation of the existing rules as @jimmyfrasche pointed out.
Another way of phrasing this is: In an assignment in all its forms (including parameter passing, "assigning" return values via a return statement, setting values in other composite literals, channel sends, there may be more) where the type of the destination is known, we can leave away the composite literal type if the value to be assigned/passed/returned/sent is a composite literal.
(We cannot leave it away in a variable declaration with an initialization expression where the variable is not explicitly typed.)
In the past we have deliberately restricted this freedom even within composite literals. This liberal form would overturn that decision.
Thus, we may want to start more cautiously. Here's a reduced form of the proposal that enumerates all permitted uses:
In addition to the existing elision rules inside composite literals, we can also elide the composite literal type of a composite value x when

x is assigned to a lhs (is used as initialization expression, that lhs must have an explicit type)
x is passed as an argument
x is returned via a return statement
x is sent to a channel

In all cases, the variable/parameter/return value/channel value type must be a composite literal type (no interfaces).
We could even reduce further and start with only 1) or 2).

The downside of limiting the cases in which elision is allowed is that the programmer must remember what those cases are.  The two extreme endpoints ("never" and "whenever the type is otherwise known") are easier to remember - and simpler to describe - due to their uniformity.

On Tue, Oct 6, 2015 at 3:17 PM, Andrew Gerrand<EMAIL_ADDRESS>wrote:

To move this proposal forward, one should apply the change to a corpus of
real Go code so that we may observe its benefits and drawbacks in context.

I agree that it would be good to apply this change to a corpus of real code
to observe its effects. I'm hunting through the stdlib to see if I can find
a package that might change in an interesting fashion. Simply eliding all
types in composite literals is uninformative, since the more interesting
uses (e.g., lightweight tuples as function parameters) require some light
refactoring.

@bcmills I would agree if we started with this as a general concept. In this case "whenever the type is otherwise known" is not sufficiently clear. For instance, in a struct comparison a == b, the types may be known but the exact rules are subtle.
This proposal is a rule that describes an exception, namely when it is allowed to elide a composite literal type. It is clearer to be explicit.

This proposal is a rule that describes an exception, namely when it is allowed to elide a composite literal type. It is clearer to be explicit.

That assumes that "eliding the type" is the exception rather than the rule.  s/allowed to elide/necessary to specify/ and the same argument applies in the other direction.
(We can explicitly enumerate the cases in which a type tag is necessary in exactly the same way that we can explicitly enumerate the cases in which it is not.)

The only other case I can think of (for consideration, if not inclusion) is eliding the type within another composite literal like
 pkgname.Struct{
   Field: {...},
 }

for
 pkgname.Struct{
   Field: pkgname.AnotherCompositeLiteral{...},
 }

FTR, I support more type elision (I proposed to use it to solve named
parameter,
see https://github.com/golang/go/issues/12296#issuecomment-134663135).
But I don't support adding a new kind of untyped literal (where they can't
be
used as const and don't have a default type -- too dissimilar from existing
untyped values.)

@minux This is a type elision proposal.  The term "untyped" in the title is misleading.
I can't tell: is there anything specific you object to this in this proposal?
(I'm not sure I support this proposal myself, but I'm trying to understand your objection.)

This is a turn of events.  I initially proposed #12296, but I found that named parameters where not a solution with current Go-idioms.
I have been in favor of this for a while; however, I have recently hit some pitfalls. I'm (now, surprisingly) leaning against this, because of legibility and common behavior:
// assume there are 2 struct types with an exported Name of type string
// they are called Account and Profile…

// Would this result in an Account of a Profile?
what := {Name: "John"}

(see http://play.golang.org/p/KM5slOe7nZ)

what := {Name: "John"}

would produce a compile error under this proposal.
(It fails the "when the literal type can be derived" constraint, which would be applied per-statement.  For this statement, the elided type cannot be derived unambiguously: it could by any struct type with a "Name" field, including an anonymous struct type.  If there is a constant or variable named "Name" in scope, it could be a map type as well.)

You would, however, be able to do the equivalent for plain assignments, as long as the variable has a concrete type:
var acc Account
acc = {Name: "Bob"}  // We already know that acc is an Account struct.

var profile interface{}
profile = {Name: "Bob"}  // compile error: profile does not have a concrete type.

@bcmills Ok… what would be the overhead on the compiler side of inferring the types and aborting if the type is ambiguous?

None.  The compiler already applies type inference for untyped nil (and untyped constants, though their default type conversion rules muddies the analogy).  E.g.,
x := nil

is a compiler error, but
var y []byte
y = nil

var z interface{} = nil
are okay.  The proposal for untyped struct literals is consistent with that.

Or another example:
var acc Account
acc = Person{Name: "Bob"}
// Compile error: cannot use Person literal (type Person) as type Account in assignment

The fact that the compiler can already complain about a type mismatch implies that the compiler already has the information to make this happen since it is expecting an Account type.

Ok… makes sense.  So is this a Go or a No?

I'm on the balance in favor of this proposal. I was already thinking about reasonable relaxations (such as elision when assigning into function-scoped variables).
There are some strange (probably useless) syntactic forms allowed with this degree of elision however, such as:
type T struct{}
T({}) // instead of T{}

type P struct { *P }
&P{{{{{}}}}}

Arguable the first form could be valid, but gofmt transformable into T{}, but the second form is the simplest representation of that structure -- it's confusing but potentially legitimate.

I'm not particularly concerned about useless syntactic forms since that's always existed, but we don't really struggle with them today:
a := ((((((0)))))) // Arguably useless and is equivalent to 0

I do have a slight concern about the stylistic wars that may happen as a result. Which of the following would be in Go style?
var m = map[int]string{1: "foo", 2: "bar"}
var m map[int]string = {1: "foo", 2: "bar"}

FTR, I support more type elison and believe it will actually improve readability on complex structures like protobufs, where the embedded struct in use is obvious based on the field names used. The inclusion of the struct type is redundant and extra noise.

Style warriors already have these options to contend over:
var i uint32 = 42
var i = uint32(42)
i := uint32(42)

I don't think untyped literals makes the situation significantly worse.

I do have a slight concern about the stylistic wars that may happen as a result. Which of the following would be in Go style?

For consistency, it seems likely that the following would be favored:
var m = map[int]string{1: "foo", 2: "bar"}

It's what you have to do if you want to use the short declaration form, and leverages right-to-left inference. Since composite types can't be constant in Go, the current const elision rules have no impact on this particular question of style.

Ok… makes sense.  So is this a Go or a No? other thoughts

This proposal would be a very significant language change. If this were to happen I expect it would take many months of consideration. By all means continue the discussion, but if you insist on a quick answer, that answer will certainly be "no".

One note on the "large parameter list as struct" use case. Let's say this proposal is accepted and people choose to use the idiom described above with anonymous struct types as arguments.
This will improve the most common way of calling the function by removing the redundant type name. But it will make it much more difficult to construct the argument to the function in a way that is not fully literal (eg if a field needs to be set conditionally) because it will be very difficult to declare an object of the argument type. While this proposal would not require libraries to define their functions using anonymous struct types as arguments, I would consider it a problem if this became popular, and it is the first example in the proposal.

@glasser: That would be a bit of an annoyance, but it would be simple enough to use variables for those conditional values.

@codeblooded
WRT your Account vs Profile example, I would hope what := {Name: "John"} would be allowed and would result in neither type but would rather be equivalent to this:
what := struct {
  Name string
}{Name: "John"}

It would only have a named type if it was assigned to a typed variable/field/parameter or cast as Account(what).
This syntax would help with the problem @glasser describes.
params := {A: 1, B: 2, C: 3}

if true {
  params.B += 40
}

Foo(params)

This could only be used for structs and not maps, of course, unless the syntax was changed to be unambiguous:
what := struct {Name: "John"}

const Name = "NAME"
what2 := map {Name: "John"}

Though perhaps parsing this would be more difficult.

Question: what would happen if a package defined a function like the following?
type unexported struct { X string }
func DoSomething(opts unexported) {}

Would you allow another package to call the DoSomething function?

That's a very interesting point.
My gut says that yes is less complicated and more regular than no.
It would be somewhat analogous to
package example
type unexported string
func DoSomething(opt unexported)

//somewhere else
example.DoSomething("I know constants are a different thing")

The real conundrum is
package example
type unexported struct {
    unexported string
}
func DoSomething(opt unexported)

//somewhere else
example.DoSomething({"this makes sense but also feels deeply wrong and bad"})

the visibility rules are unaffected by this change, so
given:
type unexported struct { X string }
func DoSomething(opts unexpected) {}
DoSomething still can't be called by external packages,
unless there is an exported way to create structs of
unexported type.
Regarding the conditional argument problem, it's
not a problem, or rather, it's actually better than status
quo. Note that to pass conditional arguments, you don't
need to declare a variable of the argument struct type.
Consider this example:
if something {
f(a, b, 1)
} else {
f(a, c, 2) // a is duplicated, should arg1 in both branches stay the
same?
}
it will change to this for f that utilizes the proposal:
arg2, arg3 := c, 2
if something {
arg2, arg3 = b, 1
}
f({A: a, B: arg2, C: arg3}) // now it's obvious, arg1 should always be a.

On Thu, Feb 4, 2016 at 8:17 PM, Kevin Gillette<EMAIL_ADDRESS>wrote:

Question: what would happen if a package defined a function like the
following?
type unexported struct { X string }
func DoSomething(opts unexported) {}
Would you allow another package to call the DoSomething function?

Yes. This is already possible so long as the parameter's type is unnamed:
somepackage.DoSomething(struct { X string }{"value"})
http://play.golang.org/p/HJ0pt8OEre

@Perelandric

I would hope what := {Name: "John"} would be allowed and would result in neither type but would rather be equivalent to this

That would be inconsistent with the proposal (which is more "allow elision of composite types" than "allow untyped composite literals").  As you noted, using it for untyped structs introduces an unpleasant ambiguity between structs and maps.  It would also not work at all for composite literals using positional fields instead of explicit field names.
Besides, it would be inconsistent: an implicitly-typed literal would mean "a struct with only the mentioned fields" in some contexts and "a struct with non-mentioned fields having zero-values" in others.  Given that Go does not (and IMO should not) apply subtyping to structs based on subsets of fields, that would make the feature surprising and non-orthogonal to the rest of the language.

@minux Honestly, I find that example more readable with a direct transliteration.
if something {
    f({A: a, B: b, C: 1})
} else {
    f({A: a, B: c, C: 2})
}

@neild: I think @minux is right about the unexported case.  The elided type should be the exact type inferred from the context in which the literal appears, not an equivalent unnamed struct type.

On Thu, Feb 4, 2016 at 8:17 PM, Kevin Gillette<EMAIL_ADDRESS>wrote:

type unexported struct { X string }
func DoSomething(opts unexported) {}
A more convoluted example which does get at your point might be:

type unexportedA struct { X string }
type unexportedB struct { Y unexportedA }
func DoSomething(opts unexportedB) {}
I don't believe it is currently possible to call that function from outside
its package. (Other than, maybe, by using reflection.)
I'm inclined to say that it if we permitted eliding the type on composite
literals, it would be less complicated to allow calling that function with
DoSomething({}) than to prohibit it.

On Fri, Feb 5, 2016 at 9:32 AM, Bryan C. Mills<EMAIL_ADDRESS>wrote:

@neild https://github.com/neild: I think @minux
https://github.com/minux is right about the unexported case. The elided
type should be the exact type inferred from the context in which the
literal appears, not an equivalent unnamed struct type.

I suppose the answer depends on whether the literals are untyped (in the
same sense as untyped constants) or merely have their type elided (but
still effectively present). I tend towards the former as being the simpler
change, but I get the impression that this is a minority viewpoint. :)

@neild Yes, that's exactly the issue.
The major problem with the "untyped literal" perspective is the ambiguity (or asymmetry) between struct field names and map keys.  Given the following program:
const animal = "gopher"
var x = {animal: "rabbit"}

what should the type of x be?  (Is it map[string]string, or struct{ animal string }?)
If that question has a well-defined answer, then the spec for untyped literals must contain an arbitrary bias toward either structs or maps and programmers will have to remember which way that bias runs.  (The situation is analogous to C++'s "most vexing parse".)
If ambiguous cases are defined as a program error, users will be confused and surprised when they accidentally run afoul of it.  (Analogous to Go's existing parsing ambiguity for composite literals in brace-delimited control-flow statements.)

@bcmills

That would be inconsistent with the proposal...

Yes, while somewhat in the spirit of this proposal, it would definitely be a new syntax.

It would also not work at all for composite literals using positional fields instead of explicit field names.

No, it would naturally be limited to named fields.

Besides, it would be inconsistent...

I'll withhold my opinions on the rest. I now think I'm too far off topic.

Would be good to implement this for constants at least.
eg
{"a", "b", "c"} => []string{"a", "b", "c"}

@splizard constants cannot be slices. See: https://blog.golang.org/constants

I think this is waiting for a detailed proposal and/or a prototype (maybe in the form of a gofmt simplifier rule). We need to see how this works in practice, surveying a wide variety of extant go code.

The title of this issue should probably be changed. The composite literals are not "untyped", but rather the type name is elided. It's more like "type inferred composite literals".

@ianlancetaylor has more ideas for potential titles for this issue.

While this would be very nice for certain cases, I can definitely see the arguments about readability. That being said, how about a more conservative proposal for while the finer points of this are being debated? What if type name ellision was allowed for anonymous structs in any use? This would help with some of the most common cases, such as embedded anonymous structs for, for exanple, JSON marshalling/unmarshalling, and things like c := make(chan *struct {arg string; ret chan<- string}), but with hopefully less impact on general readability.
For example, this would be allowed:
type Example struct {
    Inner struct {
        Val string
    }
}

func main() {
    e := Example{
        Inner: {Val: "This is an example."},
    }
}

This would not be:
type Example struct {
    Inner ExampleInner
}

type ExampleInner struct {
    Val string
}

func main() {
    e := Example{
        Inner: {Val: "This is an example."},
    }
}

Similarly, this would be allowed:
func main() {
    var e struct {val string}
    e = {val: "This is an example"}
}

That last example is a potential issue, and my biggest problem with this suggestion, but I don't think it would be too much of an issue in practice.

@DeedleFake That would lose a lot of the benefit of the proposal (e.g. for Protocol Buffers), and adding more special-cases ("only for unnamed structs types") doesn't do a lot to reduce the cognitive burden added by the feature.

I agree with @bcmills. My opinion is that the language rules should be simple and I think many of the concerns regarding readability should actually become lint checks. Suppose this proposal passes, and we understand better how people proper and improperly use it (style-wise), we can develop lint rules that guide some sane usage of type elision.

There are a lot of implications to a change like this, even though the change itself is completely syntactic sugar. It seems best to leave this for consideration as part of potentially bigger changes to the language in Go 2, rather than add one reasonable thing after another without a coherent plan for when we stop.

This seems related to #19642, in that both require a limited type inference. Perhaps it makes sense, then, to define something like a typed context which includes the following cases (I may have missed one or two, apologies if so):

The rhs of an assignment
The rhs of a variable or constant declaration that includes an explicit type declaration
A return statement
A send statement
A function argument
A composite literal (possibly other than an unkeyed struct literal)

In these cases, the type is known from context and so a composite literal (or _, if that is accepted as the zero value) is permitted.

As a micro-experience report I just ran into a situation today where I had a type that could have been
type task struct {
  // a bunch of other stuff
  accumulator map[string]struct{
   ref string
   from *T
  }
}

accumulator only exists transiently to collect data during task.run and gets processed into a useful result before returning. Much of the code only reads accumulator so the type's name doesn't matter. But I had to insert values into accumulator multiple places (in various methods of task that run calls) so I ended up writing
type (
  //exists only to have less awkward composite literal syntax
  thisIsAUselessType = struct {
    ref string
    from *T
  }
  task struct {
    map[string]thisIsAUselessType
 }
)

If I had been able to write
aTask.accumulator[k] = { ref: theRef, from: from }

the package code, as a whole, would have been clearer because I wouldn't have had to create a type at the top level.
As it is I have a note that the type is only used by task so a reader doesn't think it has some wider importance or scope. I would have much rather kept it contained.
(Equivalently, I could have written a method on task that takes two strings and a *T and only have to have written the struct definition twice. Or I could have just written out the definition each time I needed. Aside from verbosity, it would be more awkward to read than without the type as I would have been breaking idiom merely for a principle)

Here's another nice use-case: constructing a new slice with the contents of some (read-only) slice and a suffix. (That comes up when appending a call-specific options to a list of default options with APIs that accept variadic options, such as in grpc.)
See also https://github.com/golang/go/issues/24204#issuecomment-369769721.
Instead of
opts := append(append([]grpc.CallOptions{}, defaultOptions...), extraOptions...)

we could write:
opts := append(append({}, defaultOptions...), extraOptions...)

or, with https://github.com/golang/go/issues/18605:
opts := append({}, defaultOptions..., extraOptions...)

this is great

@clareyy please be aware of https://github.com/golang/go/wiki/NoPlusOne. Feel free to add responses using emoji reactions instead.

@clareyy please be aware of https://github.com/golang/go/wiki/NoPlusOne. Feel free to add responses using emoji reactions instead.

sorry ,my fault

I came here to try and express this same need in my code, and was relieved that so many other people had already described it so much better than I can.  This proposal cleanly solves one of the major sources of stutter in my code base, along with commonly requested "named parameters" feature that I miss from previous languages. So I'm really, really hoping this idea makes it into some version of Go2 on the horizon.
My specific use case is in trying to implement complex, standards-based, web APIs.  These typically have deeply nested structures and include many varying data types.  It's not feasible to improve the data structure itself (as much as I'd like to) because it's a standard API that I don't control.  I see this proposal as a practical solution to the reality that real data is messy and outside of application developers' control.
I do want to add one small thought about readability: Source code is not a static document, because modern tooling (such as gocode) includes code suggestions and tool-tips for every name on the screen.  With this proposal, if something is ever unclear or unfamiliar, it would still be easy for the reader to see the elided types by just moving their mouse over the line in question.  The current tooling in place makes this an easy win with very little downside.

I just read through this whole issue (phew!) and didn't see any discussion of the way I always use struct literals, which is with `& in front:
type T struct {
     Name string
}
var t *T
t = &T{Name: "foo"}

So how do I write that with this proposal?
type T struct {
     Name string
}
var t *T
t = &{Name: "foo"}

Does the type implication flow down through the &?

Since you can write
[]*T{
  {Name: "foo"},
}

it would follow that it would just be
t = {Name: "foo"}

Hmmm..., I'm not a fan of that.  I want to be explicit about the address operation.
But then, I'm not a fan of the missing & in your first snippet either. And that's already in the spec :(

It kind of reduces clarity a bit but editors should be able to expand the type easily, 👍 from me I think. I'd much prefer this to the "functional options" convention going around, those have horrible discovery unless they're all prefixed with With* etc, we need some stronger conventions here IMO.

This proposal is a game-changer. It can make code so much simpler to write and read. For example, tools like prisma.io which provide a library over your complete database schema could make use of this to not end up in parameter structs like UserUpdateParams, CreatePostWithoutUsers etc.

I've seen yet more people confused by the old title, so I've gone ahead with @adg's suggestion from 2016. If anyone has a better title in mind, please go ahead or leave a comment.

@DeedleFake maybe in the docs, but in auto-complete output it's usually not super clear

Yes, gopls always uses type information for autocompletion, and it ranks results that match the expected type much higher than other results.

I'm a bit icky about structs, but maps and slices are a big +1 for me. One thing that concerns me is that it may make code easier to type out, but it may hurt readability, and may confuse beginners a bit.

@turtleDev agreed, I feel like if the type signature is visible elsewhere then there's no need to repeat it (return type for example), but otherwise it might hurt things.

What is the state of this issue?

@ehsan-khaliki This issue is open.  It has neither been accepted nor rejected.

great issue!

@adg, I just read that original spec, and I think that implementing that alone would be largely sufficient to solve this issue. The original spec still required the outer type, only the inner keys and fields can be omitted. I think the idea of this being less clear was not correct. Especially to work more easily with the option struct idiom, this would simplify calling functions.

So, will it be implemented someday? I mean, this issue is opened 5 years in a row 👀

It's a proposal - it is not yet (or at all) accepted, cause there is not clear vision on how it should be done.

@DmitriyMV looks like this feature is already added, but in so specific cases like this one. I thing, this proposal don't break current language spec, and it isn't too hard to implement.
To be honest, i want to discuss, this thing is really useful)

@neild  are you still open to talk about it?

Not sure what there is for me to talk about; I still think this is a good feature, but the proposal has not (yet?) been accepted. I expect that it's unlikely to be seriously reconsidered while generics are still in progress.

@ololosha228 What @neild said. Every language change, however small adds significant cost (compiler, tools, documentation, updating libraries if desired, eduction, etc.). We don't want to take on new things while we're trying to make progress on the generics front.

I'm a little surprised at how many people are coming out in favor of this. I think the general concept of "elide type information where we can infer it" will harm the maintainability of Go code.  I believe Dave Cheney said it best:

So much has been said, about the importance of readability, not just in Go, but all programming languages. People like me who stand on stages advocating for Go use words like simplicity, readability, clarity, productivity, but ultimately they are all synonyms for one word–maintainability.

The real goal is to write maintainable code. Code that can live on after the original author. Code that can exist not just as a point in time investment, but as a foundation for future value. It’s not that readability doesn’t matter, maintainability matters more.

Go is not a language that optimises for clever one liners. Go is not a language which optimises for the least number of lines in a program. We’re not optimising for the size of the source code on disk, nor how long it takes to type the program into an editor. Rather, we want to optimise our code to be clear to the reader. Because its the reader who’s going to have to maintain this code.

If you’re writing a program for yourself, maybe it only has to run once, or you’re the only person who’ll ever see it, then do what ever works for you. But if this is a piece of software that more than one person will contribute to, or that will be used by people over a long enough time that requirements, features, or the environment it runs in may change, then your goal must be for your program to be maintainable. If software cannot be maintained, then it will be rewritten; and that could be the last time your company will invest in Go.

Most of the commentary here seems to be about writing code, not reading it, which is why I bring this up.
Some people have brought up how editors can help expose the type information.  I would rather the editor helped me write the code, with auto-completion, so I don't need its help to read it later when the code is no longer in my editor (perhaps it's on GitHub?).
Compare the two lines below.  If you're in an unfamiliar codebase, which is more helpful?
a.b <- {Bar: "baz"}
a.b <- pkg.Foo{Bar: "baz"}

It might be an unpopular opinion in this thread, but I don't think this proposal will help Go in the long run, as a language to facilitate programming at scale.  The future readers of Go code will thank us for staying explicit rather than implicit.

As a counter argument to above, the snippet below becomes more readable with type-eliding:
compress.NewReader(r, {Level: compress.BestSpeed})
compress.NewReader(r, compress.NewReaderOptions{Level: compress.BestSpeed}

You'll find examples that may get better and examples that may get worse if eliding were always performed. Personally, I prefer to at least have this choice.

As a counter argument to above, the snippet below becomes more readable with type-eliding

Your "bad case" can be improved through existing Go mechanisms.
// Alias the package name
cmp.NewReader(r, cmp.ReaderOptions{Level: cmp.BestSpeed})

// Use the functional options pattern
compress.NewReader(r, compress.WithBestSpeed())

// Or both
cmp.NewReader(r, cmp.WithBestSpeed())

You can also split your code onto two lines if it gets verbose.

@tooolbox

Compare the two lines below. If you're in an unfamiliar codebase, which is more helpful?
a.b <- {Bar: "baz"}
a.b <- pkg.Foo{Bar: "baz"}

One case in which the first version is more readable is when it is
a.b <- {Foo: "...", Bar: 123}

rather than
a.b <- pkg.FooAndBar{Foo: "...", Bar: 123}

This is not hypothetical: I often see tiny tuple structs (often with two members) that only exist for piping data through a channel, because the language does not support this more directly (unlike with function returns, say). Naming the type in these cases is mostly just stutter.
Having the choice seems better than not.

Compare the two lines below. If you're in an unfamiliar codebase, which is more helpful?
a.b <- {Bar: "baz"}
a.b <- pkg.Foo{Bar: "baz"}

Looking at these two snippets, I don't think any one of them is more readable than the other. We don't know what the type of a.b is, so we have to assume that it's the same type as what is being sent. In that regard, Both lines convey the information that some value is being sent on a channel, and we presume the types are correct or it wouldn't compile. We know nothing about type type pkg.Foo, so it being present doesn't really improve readability that much. They only thing it tells us is that a.b might be of the same type, but as a casual reader, that doesn't mean much. We don't know and can't deduce any of the properties of that type by just reading it's name.
So, besides being longer, how does the second line really improve readability?

As a counter argument to above, the snippet below becomes more readable with type-eliding:
compress.NewReader(r, {Level: compress.BestSpeed})
compress.NewReader(r, compress.NewReaderOptions{Level: compress.BestSpeed}

You'll find examples that may get better and examples that may get worse if eliding were always performed. Personally, I prefer to at least have this choice.

I find this comment to be "most correct". There are certain situations in which one way is way more readable than the other and vice-versa. Having the choice would allow programmers to use the best approach in every case (and, of course, also the wrong approach, but this can be learned)
Something similar happens with the current syntax when defining a struct literal that allows you not to write the field names, and just write the values separated by commas:
type WordCount struct {
  word string
  count int
}

// --> Specifying field names:
var goCount = WordCount{
	word:  "go",
	count: 11,
}
var golangCount = WordCount{
	word:  "golang",
	count: 8,
}
asdf
// --> Eliding field names:
var goCount = WordCount{"go", 11}
var golangCount = WordCount{"golang", 8}

In my opinion, eliding field names is almost always worse, but in certain situations where the meaning is clear, using it can improve readability a bit (by avoiding repeating the same names over an over again).
This proposal (inferred composite literals) has many more beneficial use cases than field name elision, so I'm in favor of it

It's really great to watch such a thoughtful discussion of a (relatively small) aspect of a programming language.  Go has always done a great job at this, along with having first-rate supporting tools, like go fmt and gpls.  You all are awesome.
One small point that seems relevant to me is that developers aren't building Go programs with Notepad.  Modern editors can all use gpls to include additional code insights when they're needed, to dig deeper into a type or get more information about it than is included in the current source document.
Looking at gpls in this way, Go ALREADY hides tons of type information from source code.  It's necessary to minimize repetition.  I think the issue here is "how much is too much."  Above, @alvaroloes shows other places where Go has already chosen to make struct field names optional.  I agree that "this is almost always worse" but it doesn't seem to have troubled the rest of the Go community so far.  It can be used when needed to simplify the source code, and ignored otherwise.
So, this proposal makes a lot of sense to me.  It's within the bounds of what the Go team has already determined for readability and would go a long way to "avoiding stutter" which is always a good thing.

a.b <- {Bar: "baz"}
a.b <- pkg.Foo{Bar: "baz"}

Looking at these two snippets, I don't think any one of them is more readable than the other. We don't know what the type of a.b is, so we have to assume that it's the same type as what is being sent. In that regard, Both lines convey the information that some value is being sent on a channel, and we presume the types are correct or it wouldn't compile. We know nothing about type type pkg.Foo, so it being present doesn't really improve readability that much. They only thing it tells us is that a.b might be of the same type, but as a casual reader, that doesn't mean much. We don't know and can't deduce any of the properties of that type by just reading it's name.

So, besides being longer, how does the second line really improve readability?

In the second version, you know that a.b is a channel of pkg.Foo or an interface that pkg.Foo satisfies, therefore that line contains more information.  This is helpful when you are studying an unfamiliar codebase.  Perhaps that's not something you personally want, but one can hardly argue that the lines are equivalent.
I'm seeing a pattern in the comments here:

You'll find examples that may get better and examples that may get worse if eliding were always performed.

One case in which the first version is more readable is

There are certain situations in which one way is way more readable than the other and vice-versa.

This says to me that the proposal is too broad.
It's a property of Go code that it's uniform and unsurprising to the point of being boring, an attribute decried by language designers and praised by maintenance engineers.  Broad introduction of optional type elision would muddy the concept of idiomatic Go; new engineers may opt to elide all their types, just because they can, and produce codebases that are difficult to understand.  Consigning the problem to style guides or linters is closing the barn door after the horse is gone, and not in keeping with the spirit of a language that treats unused imports as errors.  Relying on editors ignores all the other places source code can live or be seen (does your source control or diff tool have intellisense?)
However, that's not to say that all type elision is bad.  Eliding types in literals for slice elements, and for map keys & values, is superb.  That's a case where it's proven to be helpful and not harmful because "the type signature is right there, so why repeat yourself?"

This is not hypothetical: I often see tiny tuple structs (often with two members) that only exist for piping data through a channel, because the language does not support this more directly (unlike with function returns, say). Naming the type in these cases is mostly just stutter.

Perhaps this is a case for a more directed proposal.
What if type-elided structs are permitted when passed as an argument, sent on a channel, returned in a function, etc. when the argument type, channel type, etc. is itself an anonymous definition?
// This example then becomes valid.
ch := make(chan struct{
  value string
  err   error
})
ch <- {value: "result"}

// This is valid.
func sendValues(w io.Writer, values []string) error {
	var v struct {
		Result struct {
			Location []struct {
				Value string
			}
		}
	}
	for _, v := range values {
		v.Result.Location = append(v.Result.Location, {Value: v})
	}
	return json.NewEncoder(w).Encode(v)
}

// Not valid, it's the named type grpc.DialOption
opts := append(append({}, defaultOptions...), extraOptions...)

// Also not valid, unless the signature of `NewReader` defined struct{Level int}
compress.NewReader(r, {Level: compress.BestSpeed})

By adopting this rule, you can look at {Foo: bar} and know the following:

It's an anonymous struct, not a named type.
Its type definition is available in full wherever you are passing or returning or sending it.

That example proposal is off the cuff, but it illustrates a focused change that solves an ergonomics issue, without creating a footgun to broadly hide type information in the language.  The benefits of usability might actually be worth the cost in lost information.  The same can't be said for allowing general type elision.
Again, that's just an example.  The point is we should be focused and conservative about where and how we permit eliding information in source code.  Go is and should continue to be optimized for the next guy to come along, rather than for brevity.  If we go too far in removing information, we will come full circle with proposals to put it back (not quite the same, but see the subject of dot imports at https://github.com/golang/go/issues/29036 and https://github.com/golang/go/issues/29326).

There's been a lot of discussion on this proposal already. While more input is fine, I don't think we're really treading any new ground here.
Fundamentally, the arguments for and against this proposal are:

We should not do it, because eliding the top level type of a composite literal can be confusing. (I do not believe anyone has argued that it is always confusing.)
We should do it, because eliding the top level type of a composite literal can improve readabililty. (I do not believe anyone has argued that it always increases readability either.)
The decisive argument for now: We aren't making significant language changes until generics are finished. (And possibly not even then, but definitely not right now.)

I do not see an objective argument for either points 1 or 2; it's a judgement call.
One argument which has been made which I do not believe is useful is that explicit is always better than implicit. If that is the case, we should avoid writing x := f() (which implicitly assigns a type to x) as opposed to var x T = f(). If we accept that eliding types in short variable declarations is reasonable, then eliding types in other areas is not prima facie unreasonable.
But it doesn't matter for now, because no decision is being made on this either way any time soon. There are higher priorities.

In the second version, you know that a.b is a channel of pkg.Foo or an interface that pkg.Foo satisfies, therefore that line contains more information. This is helpful when you are studying an unfamiliar codebase. Perhaps that's not something you personally want, but one can hardly argue that the lines are equivalent.

I never said they were equivalent. I said the second line doesn't make the code more readable. More information does not necessarily make things more readable, sometimes it has the opposite effect. What is pkg.Foo in this case? If this is an unknown codebase ,you'd have to look up the definition of pkg.Foo, which means you already have to jump around, of if you are lucky and have the codebase in your editor, you'd have to jump to the definition. This is something you can do for any type already, including the potential elided example in the previous line. In both cases, you'd have to stop your reading and go look something up. Now lets consider if the codebase is known. You could already know what pkg.Foo is, so that helps. But you could already know what a.b is as well, which means you know what the elided type is. There could be a potential gain in readability here, but that really depends on how well you know the codebase.

It's hard to say whether a single line is clearer in isolation. The context could be something like this:
a.b = make(chan pkg.Foo)
go a.run()
a.b <- pkg.Foo{Bar: "baz"}

@jimmyfrasche
That context seems a bit too trivial and might suggest that elision is preferred.
Here's a simplified real world example with two candidates for elision:
func (c *Store) GetProducts(mapping map[string]interface{}) (products []Product, err error) {
	hash, err := getHashForMapping(mapping)
	if err != nil {
		return []Product{}, err
	}

	cache.mu.RLock()
	products, okProducts := cache.m[hash]
	cache.mu.RUnlock()
	if okProducts {
		return products, nil
	}

	chanResult := make(chan catalogueProductsJobResult)
	c.chanGetCatalogueProductsJob <- productsJob{
		hash:       hash,
		mapping:    mapping,
		site:       site,
		language:   language,
		chanResult: chanResult,
	}
	result := <-chanResult
	if result.err != nil {
		return []Product{}, result.err
	}

	return result.products, nil
}

The first candidate is the return instance. To me, that seems like a good candidate for elision, since the type is quite local, being in the signature of the function itself. So rather than []Product{}, one could write {}without sacrificing anything.
The other is the channel send line. You guys have no idea what the productsJob struct is, so the question is, is having that present helping your readability in any way? If I was new to this piece of code, my personal opinion would be that the type doesn't add anything of value here. Without the type, it would still be clear that whatever type the channel wants, has the fields present during instantiation. The type could have more fields, but that's not something that's obvious even if the type itself is present. In both cases, one would have to go to the definition of the type to see what kind of fields are available.

I feel like rather than eliding productsJob, it would be better to
give c.chanGetCatalogueProductsJob
a shorter name.
It is obvious in most cases that it is a channel of productsJob, so this is
really tedious.
I think the problem is that there is currently a Go Idom of having short
names and let types (especially function signature) give more information,
but it is not as widely adopted as other Go Idoms.
It seems this issue and that idom is aiming to solve similar issues, but is
heading different directions, which causes conflicts implicitly.
On Thu, Sep 24, 2020, 3:40 PM Viktor Kojouharov<EMAIL_ADDRESS>wrote:

@jimmyfrasche https://github.com/jimmyfrasche
That context seems a bit too trivial and might suggest that elision is
preferred.
Here's a simplified real world example with two candidates for elision:
func (c *Store) GetProducts(mapping map[string]interface{}) (products []Product, err error) {
hash, err := getHashForMapping(mapping)
if err != nil {
return []Product{}, err
}
cache.mu.RLock()
products, okProducts := cache.m[hash]
cache.mu.RUnlock()
if okProducts {
return products, nil
}
chanResult := make(chan catalogueProductsJobResult)
c.chanGetCatalogueProductsJob <- productsJob{
hash:       hash,
mapping:    mapping,
site:       site,
language:   language,
chanResult: chanResult,
}
result := <-chanResult
if result.err != nil {
return []Product{}, result.err
}
return result.products, nil
}
The first candidate is the return instance. To me, that seems like a good
candidate for elision, since the type is quite local, being in the
signature of the function itself. So rather than []Product{}, one could
write {}without sacrificing anything.
The other is the channel send line. You guys have no idea what the
productsJob struct is, so the question is, is having that present helping
your readability in any way? If I was new to this piece of code, my
personal opinion would be that the type doesn't add anything of value here.
Without the type, it would still be clear that whatever type the channel
wants, has the fields present during instantiation. The type could have
more fields, but that's not something that's obvious even if the type
itself is present. In both cases, one would have to go to the definition of
the type to see what kind of fields are available.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/golang/go/issues/12854#issuecomment-698173981, or
unsubscribe
https://github.com/notifications/unsubscribe-auth/AIAYXPTNVGM7QZEDHEN45NTSHLZWPANCNFSM4BRIOL7A
.

I feel like rather than eliding productsJob, it would be better to give c.chanGetCatalogueProductsJob a shorter name.

While I fully agree with you, this is just real-world code that's written by someone and is only meant as an illustration where type elision might be desirable (or not). I think naming is not in the scope of this issue, and shouldn't be discussed, at least not here.

As I stated in the previous comment, the current idiomatic naming
convention is based on the facts that type information is (almost) always
presented, and thus I think eliding (hiding) type information is very
related.
It seems to me that many examples, including this one you just showed,
seems to be a "naming convention vs hiding types" issue, with a few others
that feels only eliding can remove its boilerplate (e.g., repeating type
names in map literals).
On Thu, Sep 24, 2020, 11:14 PM Viktor Kojouharov<EMAIL_ADDRESS>wrote:

I feel like rather than eliding productsJob, it would be better to give
c.chanGetCatalogueProductsJob a shorter name.
While I fully agree with you, this is just real-world code that's written
by someone and is only meant as an illustration where type elision might be
desirable (or not). I think naming is not in the scope of this issue, and
shouldn't be discussed, at least not here.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/golang/go/issues/12854#issuecomment-698409292, or
unsubscribe
https://github.com/notifications/unsubscribe-auth/AIAYXPUSOA6GPAWOQWYDPU3SHNO4TANCNFSM4BRIOL7A
.

Summarizing the discussion so far
Since I want this discussion to advance and it has got very long by now, I decided to prepare a summary of all the important pros and cons so that we can have an easier time moving forward.
Since this was a long discussion I had to choose which arguments to keep but I believe I was able to select all the important ones.
I also tried to avoid adding too much of my own view in the quotes from others by being explicit in the parts where I had an opinion and always keeping a link to the original comments.
Wrapping up what has been discussed
This proposal regards extending the rules for allowing type elision to work in more cases than they already do.
It does not propose the creation of new types nor untyped constructs.
This proposal should not change in any way what we can do with the language, it will only allow the type to be omitted in situations where the compiler can infer the type from the context, and in situations where it can't, it will just cause a compilation error as it already does.
We are currently discussing a few different places where type elision could be performed:

When passing arguments to a function:Polygon({1, 2}, {3, 4}, {5, 4})

When creating nested structures:  var v struct {
  	Location []struct {
  		Value string
  	}
  }
  v.Location = append(v.Location, {Value: v})

Or:pkgname.Struct{
  Field: {...},
}

When using channels:ch := make(chan struct{
  value string
  err   error
})
ch <- {value: "result"}

When returning from a function:// Not using type elision:
return time.Time{}, err
// Using type elision:
return {}, err

When using maps:var m map[string]int
m = {"a": 1}

When using slices:type T struct {
  V int
}
var s []*T
s = {{0}, {1}, {2}}

Discussed advantages
Its easy to implement and won't make the compiler any slower
@griesemer stated that the implementation of this proposal is straightforward since this is simply a relaxation of existing rules.
@mdempsky stated that it should not add any overhead on the compiler since it already applies type inference for untyped nil.
Could improve readability
@chowey showed an example where having type elision would allow him to work with nested structs without giving them names, which he argues would make it more readable.  The example he used is the following:
func sendValues(w io.Writer, values []string) error {
	// Here is some complex JSON struct needed to provide my values to a
	// 3rd-party web service.
	var v struct {
		Result struct {
			Location []struct {
				Value string
			}
		}
	}
	for _, v := range values {
		// I'd expect Go to help me out.
		v.Result.Location = append(v.Result.Location, {Value: v})
		// COMPILER ERROR
	}
	return json.NewEncoder(w).Encode(v)
}

The editor should be able to compensate for any loss of information
@tj stated that using struct arguments would be more discoverable by the IDE than the current idiom of "functional options" which would be an advantage.

I highlighted this comment especially because I think that the "functional options" idiom is harmful to readability and this proposal might help discouraging it.

Using structs as tuples often causes Stutter
@cespare argues that since there is no easy way of creating anonymous structs or tuples in Go he often sees lines such as this one:
a.b <- pkg.FooAndBar{Foo: "...", Bar: 123}

In which the typename only causes stutter and adds no benefits for readability at all.
Reported concerns
Stylistic wars regarding how to declare variables
@dsnet is concerned with stylistic wars regarding where to put the type on declaration such as:
var m = map[int]string{1: "foo", 2: "bar"}
var m map[int]string = {1: "foo", 2: "bar"}

Note: Most people apparently don't share this concern, namely @mdempsky and @extemporalgenome replied saying they don't think this will start a war.

People might be encouraged to use more anonymous structs
@glasser is concerned with the possibility of libraries starting using anonymous structs for the arguments options, e.g.:

(I wrote the examples below to illustrate what I understood of his argument)

package example

func New(args *struct{
  Arg1 int
  Arg2 string
  // ...
}) ExampleType {
  // ...
}

Since this could make it harder to instantiate a variable of that type on the programmers' side, e.g.:
// This struct would have to be declared
// exactly like the original one:
var arg *struct{
  Arg1 int
  Arg2 string
  // ...
}

if someCondition {
  arg = &{Arg1: 10, Arg2: "foo"}
}

exampleInstance := example.New(arg)

Note: Personally I don't think this will be an issue, since it is easy to work around this problem in the same way it is easy to do it with positional arguments.

Loss in readability/maintainability
@leafbebop is concerned about the readability loss that might be caused by this proposal, if for example the variable is declared in one package as a global variable and set in another package.
@tooolbox thinks this will hurt the readability of Go code since this is optimizing writing speed not reading clarity.  So he argues against this feature for this reason.
He mentions the following example where he suggests that the explicit one is more readable:
a.b <- {Bar: "baz"}
a.b <- pkg.Foo{Bar: "baz"}

@dsnet counter argues that it depends on the specific example and shows an example where he believes the readability will actually be improved by type elision:
compress.NewReader(r, {Level: compress.BestSpeed})
compress.NewReader(r, compress.NewReaderOptions{Level: compress.BestSpeed}

This proposal conflicts with the Go idiomatic naming rules
@leafbebop argued that the current naming convention we use in Go would be hurt by this proposal since it relies on having type information readily available which could cease to be the case if this proposal was accepted.
Using elision with maps might create confusion

This concern is actually mine, even though others have discussed something very similar

When eliding the type of maps things might get a little bit confusing if we use variables as keys, and we elide the type, e.g.:
A := "a"
var m map[string]int
var x struct { A int }

// This looks the same
m = {A: 1}
// As this:
x = {A: 1}

Q&A:

Are we going to allow unexported structs to be instantiated on the client package using this new syntax?
This is a complex question since as shown by @neild in his comment this is already allowed as long as you use the full declaration of the struct instead of just the name, which is unexported, so not allowed, e.g.:
package main

import "fmt"

type unexported struct{ X string }

func F(u unexported) {
	fmt.Println(u.X)
}

func main() {
	F(struct{ X string }{"foo"}) // (this works even from another package)
}

The general opinion regarding this matter seems to be that we should implement whichever option is easier.

For pointer types, will it be mandatory to use the & operator when declaring it?
@jimmyfrasche argued that since this is already possible in Golang:
[]*T{
  {Name: "foo"},
}

it would make sense to have:
t = {Name: "foo"}

in order to declare a *T

Personally I would prefer to be explicit with the & operator, as does @randall77.

Other arguments
Restricting the scope so elision only works for anonymous structs
@DeedleFake and later @tooolbox suggested here and here that this proposal might be too broad and maybe we should restrict it so it only works on anonymous structs.

@bcmills and @dsnet disagree with this proposal since it will undermine most of the advantages and might make the feature more complicated.

Real code examples

@jimmyfrasche https://github.com/golang/go/issues/12854#issuecomment-326839729
@urandom https://github.com/golang/go/issues/12854#issuecomment-698173981

So, to be honest I wrote the review above because I wanted to add a comment in a way that would actually help us get to the solution.
So first I summarized what we currently have so that now I can discuss the point brought up by @tooolbox, which is that we might benefit from restricting the scope of this proposal.
Currently, we have a total of 6 use-cases, and below I am ordering them according to how useful I think each of them is (considering pros and cons in my mental experiments):

When passing arguments to a function
When creating nested structures
When using channels
When returning from a function
When using maps
When using slices

So one option that would probably help us have these features implemented faster would be to just create separate proposals for each of these use-cases.
I do expect that some of these use-cases might not be accepted, for example, I think that the benefit of having more type elision on maps and slices have marginal benefits and might actually, unnecessarily, decrease readability in several situations, e.g.:
A := "a"
var m map[string]int
var x struct { A int }

// ... several lines of code ...

// These 2 look the same for an uninformed reader:
m = {A: 1}
x = {A: 1}

On the other hand, I do believe that having type elision on function calls would be so useful in all the fronts that it actually has no cons IMHO, e.g.:
// With no type elision the signature of funcs with optional arguments tend to look like this:
err := posts.GetPosts(userID, nil, someBadVariableName) // what is the `nil` value?

// With type elision the creator of the `getPosts` func would be
// encouraged write a different signature for the function:
err := posts.GetPosts(userID, {
  Category: nil, // (keeping this attribute just for comparison, but this could be omitted)
  CallbackFn: someBadVariableName,
})

So in this situation, we would have:

A better function signature that won't force us to insert nil on optional arguments
No stutter, since I didn't have to write the struct name which would be something like posts.GetPostsOptions
And now we have more information for the reader, not less: we do know now that the second argument is the category, and that someBadVariableName is actually a callback.
We also have even more information regarding the decision of moving only some arguments to the struct: It's likely that the userID is mandatory and that the ones inside the struct are optional arguments.

Conclusion
So what I want to say is that I would prefer to have these use-cases separated so that I could defend the ones I find more useful and have them available sooner.

So one option that would probably help us have these features implemented faster would be to just create separate proposals for each of these use-cases.

I rather firmly feel that, as I described in the proposal at the top of this issue, the simplest and most consistent option--simpler and more consistent than the current language specification, even!--is to say that a composite literal with no type specified is assignable to any variable of array, slice, or map type, or pointer to same. (Note that under the nomenclature of the language specification, "assignable to a variable..." covers passing parameters to a function, sending values to a channel, etc.)
Enumerating the cases under which the type may be omitted is, in contrast, inherently inconsistent.

@neild is to say that a composite literal with no type specified is assignable to any variable of array, slice, or map type, or pointer to same

Agree, but partially:
Imagine you have something like this:
var x map[string]interface{} = {
    "value": {
	A: 123,
	B: "abc",
	C: true,
    },
}

Will this be the correct definition of the variable? If so, how does go know that the interface contains the struct SomeStruct and not a VerySimilarStruct? The main point here is not that you need to write code as simply as possible, but that you do not write the same information from time to time: when we define
var someMap map[string]interface{}
someMap = map[string]interface{}{"a": 1, "b": true}

then we overcomplicate life of typical go developer: the compiler can determine the type of the value that is assigned to this map without us, what is the point of writing the type of the map twice?
BUT. If we are talking about interface values in slices, maps, etc., like error numeric, then there is no reason to force the compiler to predict the value. just because the compiler is not a baba vanga, it does not predict your intentions.
Also important question: we can make pointer to interface like *error (idk why, but we can). So, will var err *error = {Text: "some text of error"} f.e. will be correct? we defined pointer too.

Also important question: we can make pointer to interface like *error (idk why, but we can). So, will var err *error = &{Text: "some text of error"} f.e. will be correct? we defined pointer too.

I would actually expect that you would not need the &, since this works today:
var _ = []*slice{}{{}, {}}

Imagine you have something like this:
var x map[string]interface{} = {
    "value": {
	A: 123,
	B: "abc",
	C: true,
    },
}

Based on @neild's summary:

a composite literal with no type specified is assignable to any variable of array, slice, or map type, or pointer to same.

Your example is not covered; also, there is no type that can be inferred for an interface, other than an untyped nil; so imo, your example is undefined and should not be allowed.

@quenbyako I think this has already been discussed, type inference is already made by the compiler when it is possible, we don't plan to change how it works we only want to relax the rules so we can have some benefits. But in your first example with a map, and in your last example with the error there is no way the compiler would know the dynamic type if you don't write it down yourself so it would just cause a compiler error.
@neild I understand your argument, I agree that it would be simpler to implement it this way, but I don't think this would increase the complexity of the language to anyone who is learning it, since this is just a syntax sugar I imagine people will learn about it in two ways:

Observing the usage examples of other packages on its README files
Guessing and making experiments to see if the compiler accepts that use-case

Both situations are orthogonal to the process of learning the language the first time, so I think that the criteria of whether this would improve of worsen the readability is more important and is actually what is going to define whether we can add this to the language or not. Meaning that we need to come up with strategies that will allow us to harness the maximum readability in most situations with the minimum loss of it in the remaining cases.
And if we are going to keep discussing in terms of all the use-cases or none, we won't be able to fine-tune our discussions to that degree.

@arnottcr yeup, that is exactly what i wanted to say)
@VinGarcia just wanted to fresh up knowledge of this specific case 🙃

Uhm, I just noticed one problem we'll have to keep in mind if/when implementing this proposal. I opened the go2go playground where we can play with generics and the initial code made me realize that omitting the type on generic functions might be problematic, the very first example at https://go2goplay.golang.org/ illustrates the problem very well:
func Print[T any](s []T) {
	for _, v := range s {
		fmt.Print(v)
	}
}

func main() {
	Print([]string{"Hello, ", "playground\n"})
}

Here if we omitted the []string{"Hello, ", "playground\n"} I think the compiler would have no way of knowing the type in the way it normally does. You may argue that it knows it's a []string because it contains strings but this is not written anywhere and it could as easily be interpreted as []interaface{}.
Maybe the best solution for this would to just throw a compile error saying it can't infer the type or something like that, otherwise we would be expanding the scope of this proposal.

@VinGarcia
IIRC, the generics proposal only infers the parametric type when feasible. For all other cases, the user needs to specify it as
Print[[]string]({"Hello, ", "playground\n"})

Yeah, but I think that this would still complicate the implementation of this feature, because this is a new situation that didn't existed before, i.e. without this proposal, the compiler error could happen only if the generic type T is not in the function signature meaning that it can't infer the type T from the arguments, now even if the type T is in the function signature it will also need to check whether the type was omitted or not.
I don't know the actual compiler's code, so I am just guessing, but I think it is important to have this in mind.

@urandom lol this looks REALLY ugly 😂

I would expect that if this and generics land, we could consider allowing this too:
Print([]string{"Hello, ", "playground\n"})

I would expect that if this and generics land, we could consider allowing this too:
Print([]string{"Hello, ", "playground\n"})

I'm confused. That code already appears in the example code at go2goplay.golang.org.

We are drifting off topic, so sorry for contributing; but I thought that generic type elision was not an mvp feature, and it would be considered at a later date?

Print[[]string]({"Hello, ", "playground\n"})

It should look like this instead
Print[string]({"Hello, ", "playground\n"})

because in the definition of Print, the argument is []T, which is string in this case.

Fundamentally, the arguments for and against this proposal are:

We should not do it, because eliding the top level type of a composite literal can be confusing. (I do not believe anyone has argued that it is always confusing.)
We should do it, because eliding the top level type of a composite literal can improve readabililty. (I do not believe anyone has argued that it always increases readability either.)
The decisive argument for now: We aren't making significant language changes until generics are finished. (And possibly not even then, but definitely not right now.)

I do not see an objective argument for either points 1 or 2; it's a judgement call.

One objective argument is that (2) allows users to make the decision (since the user can always decide not to elide the type), and allows different users to make different decisions. (1) entails the Go team making the decision universally on behalf of all users.

Now that Generics is available I was wondering whether it would be possible to continue this discussion. Not that I have anything to add, but it might be beneficial to have some feedback from the Go team about the feasibility of this feature and whether it is fine as is or if we might need to refine it a little bit more.
I see 4 possible paths moving forward:

The proposal is not accepted (I hope not)
The proposal could be fully accepted
Problems with the current proposal could be pointed out so we can refine it
The proposal could be broken into smaller proposals, given that type inference can happen in several different contexts and maybe some of these contexts might have more benefits than costs while others might have more costs than benefits (although I would not be against a full approval).

This would make slog's API nicer. (See #56345.)
Instead of
slog.Info("msg", "a", "b", "c", "d")

which many people don't like because it's hard to tell what are keys and what are values, we could instead write
slog.Info("msg", {"a", "b"}, {"c", "d"})

which is marginally more verbose but much easier to read.

This would make slog's API nicer. (See #56345.) Instead of
slog.Info("msg", "a", "b", "c", "d")

which many people don't like because it's hard to tell what are keys and what are values, we could instead write
slog.Info("msg", {"a", "b"}, {"c", "d"})

which is marginally more verbose but much easier to read.

The same is potentially true of GRPC’s metadata.Pairs

I personally really like the fact that this could be a mechanism to enable named parameters to functions:
This can be considered a counter point to @glasser being conscerned about anonomous structs as function arguments.
func parameterisedParams(struct {
	one    int
	two    int
	three  int
	length int
	width  int
}) {}
func SomeFunc() {
	parameterisedParams({one: 1, two: 2, three: 3, length: 4, width: 5})
}

This is a very simple way of adding this feature to go without having to add any more new syntax.

I think that would be pretty useful when you are dealing with JSON APIs with lots of nested objects.
Rewritting all the nested structs is completely unfeasible so I had to type each "substruct".

@rsc Please can you explain why is this marked as V2?
Im very sure this could be done in V1 with backward compatibility ?

@KevinJCross v2 is a label applied to all language change proposals to facilitate triage and searching. It doesn't have any bearing on whether the proposal can be accepted before Go 2.

This would make slog's API nicer. (See #56345.) Instead of
slog.Info("msg", "a", "b", "c", "d")

which many people don't like because it's hard to tell what are keys and what are values, we could instead write
slog.Info("msg", {"a", "b"}, {"c", "d"})

which is marginally more verbose but much easier to read.

That is not the case for the proposal. slog.Info accepts (string, ...any), so no way to recognize the type of {"a", "b"}.
And, even if we were to restrict slog.Info to signature like (string, ...slog.Attr), slog.Attr as of now is
struct {
	Key   string
	Value slog.Value
}

So, if we elide {"a", "b"} to slog.Attr, "a" becomes Key and "b" should become Value. But slog.Value is somewhat complex struct with no public fields, so still no way to elide slog.Info arguments.
Now, even if we suppose slog.Value to be just any type, {"a", "b"} still could not be elided, since there are two possibilities:

Value is string("b"), corresponding to slog.String("a", "b") for {"a", "b"}
Value is any("b"), corresponding to slog.Any("a", "b") for {"a", "b"}

and {"a", "b"} still could not be elided.
which is similar to argument made in https://github.com/golang/go/issues/12854#issuecomment-823219644

This is an ongoing difficulty for me. I tend to make a lot of "config files" in regular go code, because why map from yaml or whatever to go, when I can just write it in go and get compile time checks, code completion, etc.
However, there are a lot of cases where type inference should let me elide the types and yet I can't, leading to less readable code.
For example:
type User struct {
    Email string
}
type Group struct {
    Leader User
}

var Groups map[string]Group = {
    "devs" : {
        Leader: User{
            Email<EMAIL_ADDRESS>        }
    }
}

The go tool was modified so maps and slices don't need to specify the type when it's a struct, but struct fields still need to have the type specified when the field is a struct.  Why? If the field name is obvious in context, you really don't need the type name.
This seems perfectly legible to me:
var Groups map[string]Group = {
    "devs" : {
        Leader: {
            Email<EMAIL_ADDRESS>        }
    }
}

And certainly, the lack of User after Leader is no less expressive than the lack of Group after "devs".  You don't need to see the Group type to see that the Leader has an email of<EMAIL_ADDRESS>If that's all you're writing anyway, adding User is not really adding any information.
This gets compounded with larger structs, especially if you're making an anonymous struct as a field in another struct.
type Config struct {
    Telemetry struct {
       Port int
       URL string
       Password string
    }
    DB struct {
       ConnectionStr string
       Password string
    }
}

I like making anonymous structs as fields inside another struct because the whole of the struct definition is all in one place. You don't have to jump around to see what each field's sub-fields are. It uses the anonymous structs as namespacing, with short, clear field names in the anonymous structs. There's very clearly no logic tied to the struct fields, because they're not defined as individual types. It's just data representation.
The problem with anonymous struct fields right now is that constructing that type requires re-specifying the whole structure of the anonymous struct, even though the compiler already knows what it has to be.
cfg := Config {
    Telemetry: struct {Port int; URL string; Password string} {
        Port: 8888,
        URL: ProdTelemUrl,
        Password: os.Getenv("TELEMETRY_PASSWORD"),
    },
    DB: struct {ConnectionStr string; Password string} {
        ConnectionStr: ProdDBUrl,
        Password: os.Getenv("DB_PASSWORD"),
    },
}

If the go tool could elide the struct definitions this could be:
cfg := Config {
    Telemetry: {
        Port: 8888,
        URL: ProdTelemUrl,
        Password: os.Getenv("TELEMETRY_PASSWORD"),
    },
    DB: {
        ConnectionStr: ProdDBUrl,
        Password: os.Getenv("DB_PASSWORD"),
    },
}

This is both easier to read and easier to write, and matches up really nicely with the struct definition.
To avoid the verbose retyping of anonymous struct types, people either make separate types, which make you jump around just to see what all the fields for this config are, and make you wonder if there's actual logic on that TelemetryConfig struct, or they put all the fields in the base struct and prefix the names with their namespace, which is repetitive and harder to read because of all the long field names.
Please update the go tool to allow us to elide types when constructing struct literals with fields that are also structs. It would make Go almost as concise as yaml, but without all the yaml footguns.
@ianlancetaylor @rsc

It uses the anonymous structs as namespacing, with short, clear field names in the anonymous structs.

It would be nice to be able to grab an anonymous struct type out of the parent:
type Config struct {
    Telemetry struct {
       Port int
       URL string
       Password string
    }
    DB struct {
       ConnectionStr string
       Password string
    }
}

var db Config.DB

// do something with db

@natefinch I really think this is in the eye of the beholder, whether the struct type info is helping or not. FWIW, it makes your first example more legible to me if there is an explicit User. I remember a couple of times when I was annoyed to be forced to spell out type information in similar situations, but it was my code and I knew it well.

@ydnar
It's not really what it was designed for, but some of the proposed solutions in #34515 could theoretically allow that, such as var db typeof(Config.DB).

@ydnar
It's not really what it was designed for, but some of the proposed solutions in #34515 could theoretically allow that, such as var db typeof(Config.DB).

Inspired by Swift’s use of empty enums as namespaces, which can allow for less-mangled type names. I don’t want to hijack this proposal and will discuss elsewhere.

Here's a really good comparison. Almost identical definitions, but the struct form doesn't let you elide the type:
type User struct {
	Email string
}
type GroupMap map[string]User

type GroupStruct struct {
	Leader User
}

func main() {
	_ = GroupMap{
		"Leader": {
			Email<EMAIL_ADDRESS>		},
	}

	_ = GroupStruct{
		Leader: User{
			Email<EMAIL_ADDRESS>		},
	}
}

@VinGarcia — Your summarizing comment from almost 3 years ago did not include as the reason for eliding this is why this is probably my number 1 feature request for Go, and that is for use when passing options via an options struct:
package main

type StandardWidgetOptions struct {
	Foo int
	Bar string
	Baz bool
}

type StandardWidget struct {
	Name string
	Opts StandardWidgetOptions
}

func NewStandardWidget(name string, opts StandardWidgetOptions) *StandardWidget {
	return &StandardWidget{
		Name: name,
		Opts: opts,
	}
}

func main() {
	// Current malaise
	w1 := NewStandardWidget("my_widget1", StandardWidgetOptions{
		Foo: 10,
		Bar: "Hello",
		Baz: false,
	})

	// Hoped for future
	w2 := NewStandardWidget("my_widget2", {
		Foo: 25,
		Bar: "Sweet",
		Baz: true,
	})

	// Or, also!
	w3 := NewStandardWidget("my_widget2", {Foo: 100,Bar: "World",Baz: true})

	// Stop Go complaining about unused vars
	print(w1,w2,w3)
}

Just commenting in hopes to keep the dream alive... :raised_hands:

I read through this thread, and I believe I have a different use case.
I want to create a slice of structs as test cases that I want to loop over for a test that I am writing. I am using a slice of structs so that the test value passed to the function is near the expected returned value.
With current syntax, it looks something like:
type testData struct {
	test     []int
	expected []int
}

var tests = []testData{
	{[]int{0, 0}, []int{0, 0}},
	{[]int{0, 1}, []int{1, 0}},
}

I have 18 such test cases in this slice. In my opinion, all of the []ints that I would have to write are visual clutter when they can be elided from the struct definition.
If I want to use this style, for some reason the below is also valid syntax:
type testData [2][]int

var tests = []testData{
	{{0, 0}, {0, 0}},
	{{0, 1}, {1, 0}},
}

and in this case the types are elided, but with this, the gopls language server can no longer show me the field name hints that clearly label each test case's test data and expected values.

I agree with @mikeschinkel and @humdogm. Zig is solving that problem with Anonymous Struct Literals. They allow omitting the struct type of the literal, and can be coerced to an actual struct type if the coercion is completely unambiguous.

Currently, the functional option pattern is often introduced as a way to achieve optional arguments in Go. However, I feel this approach is not appropriate because it introduces excessive complexity. If zero values were sufficient, we could simply use structs instead of functional option patterns (FOPs), but this is not widely recognized at the moment.
If users are frustrated by having to write long struct names for every call and think, "Go has no way to achieve optional arguments except through FOP!" then I believe the introduction of this feature could lead many programmers to make better design choices.
I also believe that being able to write shorter code, not only for FOP but also for functions that take tuples, such as in logging with log/slog, could lead to better designs as well.

I am in favor of this proposal and will try to answer the concerns that have been raised.
The key of map initialization and the field name of the structure are indistinguishable
This is probably the most critical issue, in my opinion, as only structs can be untyped, or map and slice would need to have a separate syntax.
m := map{keyStr: "value"}
s := []{idxInt: "value"} // []string
v := {value: "value"} // struct{value string}

Implicitness has a negative impact on readability
I don't think this is a big problem; Go already has a lot of "if you don't know the definition, you don't know the type" situations.
x := f() // what's the type of x?
ch <- {value: "value"} // what's the type of ch?

The key of map initialization and the field name of the structure are indistinguishable
This is probably the most critical issue, in my opinion, as only structs can be untyped, or map and slice would need to have a separate syntax. I do not consider it that important to be able to write a map or slice that way, compared to being able to shortly define structures that have important uses, such as tuples and optional arguments.
m := map{keyStr: "value"}
s := []{idxInt: "value"} // []string
v := {value: "value"} // struct{value string}

Unless I'm misunderstanding something, that syntax overlap doesn't exist. There's no unquoted shortcut syntax for map keys currently.

@DeedleFake it's not "unquoted keys", it's the fact that the key can be a variable: https://go.dev/play/p/TvEURZjvfHR

It's already the case that you can't tell what the key of a composite literal is at parse time.
f(T{
  Key: "value", // Is Key a value, or a field name? We don't know until we know what T is.
})

Eliding T from the composite literal doesn't add any additional parsing ambiguity. We already need to defer interpretation until typecheck.

Hi,
I'm sorry to jump into the conversation like a hairsbreadth, but having just discovered this language feature, I investigated to find out when and it which release it was introduced.  But I am struggling to find out, and feel confused about the fact that this issue is still open while the type inference in composite literal seems to be implemented in go1.22.
I'd be grateful to any soul who could enlighten me on this matter.

This proposal is neither accepted nor implemented. The part of the specification you're referencing is what this proposal proposes to amend.

That would be specially useful for nested structs like for tests, json DTOs, and so on:
tests := []struct{
  desc string
  val1 int
  val2 bool
  val3 string
  user struct{
    id string
    name string
    address string
  }
}{
  {
    desc: "test name",
    val1: 123,
    user: {
      id: "user id",
      name: "name",
    },
  },
}

instead of:
(code snippet elided)

Or, as a sensible gopher would write,
type testUser struct{
  id string
  name string
  address string
}
tests := []struct{
  desc string
  val1 int
  val2 bool
  val3 string
  user testUser
}{
  {
    desc: "test name",
    val1: 123,
    user: testUser{
      id: "user id",
      name: "name",
    },
  },
  {
    desc: "test name",
    val1: 123,
    user: testUser{
      id: "user id",
      name: "name",
    },
  },
  {
    desc: "test name",
    val1: 123,
    user: testUser{
      id: "user id",
      name: "name",
    },
  },
}

Which, in this very specific case, is not really that awful.
 Which is not optimal.

@igorcafe, you're making a very valid point.
But it has already been made before, and, sadly, does not contribute much more new information to the discussion here than a simple :+1:  on the relevant prior posts. Be sure you haven't missed other relevant ideas in earlier posts! :wink:

@antichris problem in that is that you need to create a new type that will only be used as a "nested type", which adds unnecessary complexity to the code, when the real relevant type is the top level type

Similar to the last few posts, one of the major places where this has been pinching me lately is table tests with subtables. I'll have tests that are identical except for some shared config that would be awkward to manage if duplicated in dozens of consecutive rows or as separate tests that are identical except for some configuration. What I'd really like to do is just:
var table = []struct{
  /* shared config */
  subtable []struct {
    /* input/expected */
  }
}{
  {/*...*/, {
    {/*...*/},
  }},
}

(There are many, many ways to write that now but they all involve adding extraneous bits that at best don't add anything to the code and at worst litter it with distractions.)

Similar to the last few posts, one of the major places where this has been pinching me lately is table tests with subtables. I'll have tests that are identical except for some shared config that would be awkward to manage if duplicated in dozens of consecutive rows or as separate tests that are identical except for some configuration. What I'd really like to do is just:
var table = []struct{
  /* shared config */
  subtable []struct {
    /* input/expected */
  }
}{
  {/*...*/, {
    {/*...*/},
  }},
}

(There are many, many ways to write that now but they all involve adding extraneous bits that at best don't add anything to the code and at worst litter it with distractions.)

Just another vote for this in table drive tests.  While working with Go, this has always been painful and the proposal would make this much nicer.


V2: pnpm install fails when pnpm-lock.yaml is not present

On a fresh clone of branch a0e5847, if I remove pnpm-lock.yaml so as to regenerate it and run pnpm install, the installation fails.
[NEXTRA-ESM] DTS Build start
│ [NEXTRA-TYPES] DTS Build start
│ src/plugin.ts(173,43): error TS2345: Argument of type 'string | undefined' is no
│   Type 'undefined' is not assignable to type 'string'.
│ Error: error occured in dts build

The committed lock file allows a successful pnpm install, but apparently can't be regenerated. This blocks my project from creating its own variant of the lock file.

Confirmed the same on the latest core.


Eliminate useless RDD map when the input columns match the target col…

Eliminate useless RDD map when the input columns match the target columns during Spark-Vector load.
Also replaced sc.parallelize with sc.range when generating dataframes with 0 columns (for count(*) type of queries).

ship it

ship


Cross-platform, but does not work on Windows

well duh, of course SH script won't work on Windows :) unless of course you're using WSL or Cygwin
for this you will need a proper implementation, for which I'm not interested, at least for now
in this case, cross-platform means that it can run on any architecture (x86, ARM, etc) since the assembly instructions are almost the same, but yeah, theoretically it can run almost everywhere, granted that AES is done by the CPU itself, regardless of the OS


Allow the definition of multiple PatientRetriever configurations and make them assignable for each clinic

This way, we can decide where the information for a clinic should be fetched from. Multiple configurations should be possible both for the pseudonymization service and the patient data retriever.
Then for a clinic, a specific patient retriever, a specific pseudonymization service or the plain case number service can be activated. Depending on what was activated for a clinic, said options should be shown in the frontend for a user for the clinic.

Merged with #102