Why not tuples?

4,696 views
Skip to first unread message

Jonathan Amsterdam

unread,
Nov 20, 2009, 9:09:29 AM11/20/09
to golang-nuts
Tuples are too obvious a feature to have not been considered and
rejected by the Go team, so I'm wondering why. Perhaps this could be
added to the FAQ.

The advantages of having tuple types:

- They replace the multiple-return-value feature with something that
is first-class.

- No overhead for the usual case of "a, b = f()". The code for f
places the return values on the stack, and the code generated at the
call site populates the variables, exactly as now.

- They remove the restriction on single-valued expressions as
arguments to functions (or rather, everything becomes a single-valued
expression) and eliminate the special case of f(g(..)) where g is
multiple-valued.

- It is still overwhelmingly likely that you will not be able to
ignore an error return. E.g. currently this fails to compile if write
returns a second, error value:
n := write(...);
Thus a sloppy programmer can't ignore the error. With tuples, it
would compile and n would be a 2-tuple, but if you tried to use n as
an int, you'd get a type error at compile time.

Of course, Go also allows simply
write(...);
so inability to ignore errors may not be a compelling point either
way.

- Their existence wouldn't interfere with the multiple-return idioms
like non-blocking channel read. That is just syntax, not a true multi-
value return. The one screw case I see is
a, b := m[k]
where m is a map to 2-tuples. This looks like destructuring assignment
of the value, but is actually a special case. The most conservative
solution is to disallow destructuring in a map assignment.

- As a side benefit, they could be a key type for maps, dramatically
increasing the possible key types. If I understand the objections to
equality methods for structs, they are:

1. There are many ways one might want to define equality, and the
language shouldn't pick one and privilege it by calling it ==.
2. A naive implementation would loop with cycles and an always-correct
one would be expensive.

#1 doesn't apply to tuples, because they are not user-defined types,
so the right equality predicate is no more contentious than is that
for strings. #2 can be eliminated if we make tuples immutable. (That
also helps with them being map keys, though immutability is not a
reason given for disallowing other types as keys.) Of course, a tuple
containing a non-comparable value would itself be non-comparable.

TerryP

unread,
Nov 20, 2009, 10:15:39 AM11/20/09
to golang-nuts
Personally, I don't see a real benefit, that out weighs the Go team
shoving more syntax down our gullets--compared to what the language
already offers `built in`.

--
TerryP.
Just Another Computer Geek.

Joseph Stewart

unread,
Nov 20, 2009, 10:36:08 AM11/20/09
to golang-nuts
Being a Lua user, I've grown accustom to multiple return values... I'd respectfully like this feature to remain.

-joe

Jonathan Amsterdam

unread,
Nov 20, 2009, 10:45:02 AM11/20/09
to golang-nuts
> Being a Lua user, I've grown accustom to multiple return values... I'd
> respectfully like this feature to remain.

How would adding tuples remove any multiple-return-value
functionality? You'd still be able to write "a, b = f()", and you'd
still be able to write "return x, y". Is there another use of multiple
return values I'm missing?

Ben Tilly

unread,
Nov 20, 2009, 11:47:51 AM11/20/09
to Joseph Stewart, golang-nuts
On Fri, Nov 20, 2009 at 7:36 AM, Joseph Stewart
<joseph....@gmail.com> wrote:
[...]
> Being a Lua user, I've grown accustom to multiple return values... I'd
> respectfully like this feature to remain.

Ruby demonstrates that there is no contradiction. If you add in the
ability to flatten a tuple, or decide when to gather several things
into a tuple, then the two features co-exist perfectly

Inventing some syntax:

// explicit declaration
type SomeTuple [string, int] tuple;

// Interaction with multiple returns
myTuple := [ function_returning_multiple_values() ];
this, that := @myTuple;

Cheers,
Ben

roger peppe

unread,
Nov 20, 2009, 12:51:02 PM11/20/09
to Ben Tilly, Joseph Stewart, golang-nuts
2009/11/20 Ben Tilly <bti...@gmail.com>:
> On Fri, Nov 20, 2009 at 7:36 AM, Joseph Stewart
> <joseph....@gmail.com> wrote:
> [...]
>> Being a Lua user, I've grown accustom to multiple return values... I'd
>> respectfully like this feature to remain.
>
> Ruby demonstrates that there is no contradiction.  If you add in the
> ability to flatten a tuple, or decide when to gather several things
> into a tuple, then the two features co-exist perfectly

limbo (one of go's antecedents) also had this feature.

i'm not quite sure why tuples were left out.

Peter Froehlich

unread,
Nov 20, 2009, 1:00:34 PM11/20/09
to roger peppe, Ben Tilly, Joseph Stewart, golang-nuts
Overall it seems that Go is "ripe" tor tuples. Yes there would have to
be a few changes, but hey, you are not seriously thinking of never
changing the language again, eh? :-D In any case, if there's a
compelling reason not to have them, it would be nice to have an FAQ
entry for it. :-D
--
Peter H. Froehlich <http://www.cs.jhu.edu/~phf/>
Senior Lecturer | Director, Johns Hopkins Gaming Lab

TerryP

unread,
Nov 20, 2009, 6:54:48 PM11/20/09
to golang-nuts


On Nov 20, 6:00 pm, Peter Froehlich <peter.hans.froehl...@gmail.com>
wrote:
> Overall it seems that Go is "ripe" tor tuples. Yes there would have to
> be a few changes, but hey, you are not seriously thinking of never
> changing the language again, eh? :-D In any case, if there's a
> compelling reason not to have them, it would be nice to have an FAQ
> entry for it. :-D

Overall, it's probably something they felt wasn't required at this
stage of the game; when arrays, slices, and basic data structures are
already here. It'll probably be added some day if the developers see
it as a necessity.


Personally, I'm just happy to have a decent string and map type built
in ;)

--
TerryP
Just Another Computer Geek

ziyu_huang

unread,
Nov 20, 2009, 8:23:43 PM11/20/09
to golang-nuts
I like multiple return, when language support it, tuple is meaningless
to me.
Why add complex syntax when there already have simple one ?
A simple struct can do the same thing if you insist to ..

type ATuple {
rv1 int;
rv2 int;
rv3 string;
}

func tuple_return_func() {
a, b int;
...
return ATuple { a, b, "some string"};
}

ret := tuple_return_func();



On 11月21日, 上午12時47分, Ben Tilly <bti...@gmail.com> wrote:

Jon Harrop

unread,
Nov 20, 2009, 11:10:22 PM11/20/09
to golan...@googlegroups.com
On Saturday 21 November 2009 01:23:43 ziyu_huang wrote:
> I like multiple return, when language support it, tuple is meaningless
> to me.
> Why add complex syntax when there already have simple one?

What is complex about tuple syntax?

> A simple struct can do the same thing if you insist to ..
>
> type ATuple {
> rv1 int;
> rv2 int;
> rv3 string;
> }
>
> func tuple_return_func() {
> a, b int;
> ...
> return ATuple { a, b, "some string"};
> }
>
> ret := tuple_return_func();

With tuples:

func tuple_return_func() {
a, b int;
...
return (a, b, "some string");
}

ret := tuple_return_func();

Isn't that simpler?!

--
Dr Jon Harrop, Flying Frog Consultancy Ltd.
http://www.ffconsultancy.com/?e

ancientlore

unread,
Nov 20, 2009, 10:22:34 PM11/20/09
to golang-nuts
Personal opinion: tuples and multiple return values are quite
different. A whole type system around tuples has to handle these
anonymous types, sort of like structures but without a name or element
names. And you'd need a way to declare the type and a syntax for
testing the type, (i.e. obj.((int, string, Foo)) ).

I also wonder how well tuples fare in really large programs since they
aren't very self-documenting. It seems like the main inconvenience of
just using a struct is that you need to declare it first.

Aren't tuples also most useful in languages that have match
expressions instead of (or in addition to) basic value comparisons?

To really have the full benefits of tuples, it seems like quite a few
features would need to be added to the language. It's much more than
just the syntax of a composite literal.

-Mike

Jon Harrop

unread,
Nov 20, 2009, 11:38:50 PM11/20/09
to golan...@googlegroups.com
On Saturday 21 November 2009 03:22:34 ancientlore wrote:
> Personal opinion: tuples and multiple return values are quite
> different. A whole type system around tuples has to handle these
> anonymous types, sort of like structures but without a name or element
> names. And you'd need a way to declare the type and a syntax for
> testing the type, (i.e. obj.((int, string, Foo)) ).
>
> I also wonder how well tuples fare in really large programs since they
> aren't very self-documenting. It seems like the main inconvenience of
> just using a struct is that you need to declare it first.

Yes.

> Aren't tuples also most useful in languages that have match
> expressions instead of (or in addition to) basic value comparisons?

Yes.

> To really have the full benefits of tuples, it seems like quite a few
> features would need to be added to the language. It's much more than
> just the syntax of a composite literal.

Yes.

Looks like tuples come on the wish list below generics, type inference and
pattern matching then. ;-)

ziyu_huang

unread,
Nov 20, 2009, 11:03:34 PM11/20/09
to golang-nuts
I make this example is not try to solve tuple, it try to example use
tuple instead of mutliple-return
is *much* stupid.

Maybe Tuple has other useful thing, but you definite pick the wrong
example.

roger peppe

unread,
Nov 24, 2009, 7:34:47 AM11/24/09
to Ian Lance Taylor, golang-nuts
2009/11/23 Ian Lance Taylor <ia...@google.com>:
> One issue with tuples is how they play with comma-ok statements.
>
>        c := make(chan {int,bool});
>        v1, ok := <-c;
>        v1, ok1, ok2 := <-c;

this isn't a problem, because you could distinguish
between

v1, ok := <-c;

and:

(v1, ok) := <-c;

i.e. tuples are always written in brackets.

thus your example becomes:

c := make(chan (int, bool));
v1, ok := <-c;
(v1, ok1), ok2 := <-c;

both the receives are non-blocking.

v1 := <-c;
(v1, ok1) := <-c;

specifies two blocking receives.

i've no idea how that mucks up the grammar though - maybe
that's the real reason for not having tuples.

Valentin Kasas

unread,
Dec 12, 2009, 9:09:12 AM12/12/09
to golang-nuts
Hi everyone

Is it a mistake to say that tuples are something like unnamed structs
with unnamed fields only ?
If not, you may consider using the ... argument to create some go
equivalent to tuples.

I've already wrote a toy package that uses the following to create
tuples and play around with
(comparing types of different tuples, extracting fields, flatten a
nested tuple in a list, ...)


type Tuple interface {}
func NewTuple(args ...)Tuple{ return args }


The only drawback is that you must type 'NewTuple' before each opening
parenthesis :

a := 0.0;
b := make(chan int);
t := NewTuple( a , "hello world" , NewTuple( 3.141592, b) , NewTuple
()) ;

will gently create an object wich static type is schematically struct
{ float; string; struct{ float; chan int} ; struct {} }
but you don't want to "see" (and type) these explicit function calls.

If we could define a single unnamed func that would do the job of
NewTuple :

func ( args ...)Tuple { return args }

it would be simply called by ( foo ) and so we're done.
But i'm not sure that such ( foo, bar, bash ) floating in the code
will never lead to ambiguities with the existing syntax.

befelemepeseveze

unread,
Dec 12, 2009, 4:33:38 PM12/12/09
to golang-nuts
On 12 pro, 15:09, Valentin Kasas <valentin.ka...@gmail.com> wrote:
> If we could define a single unnamed func that would do the job of
> NewTuple :
>
> func ( args ...)Tuple { return args }
>
>  it would be simply called by ( foo ) and so we're done.

Just being curious what is and what is not possible with ..., I tried
(I know this is not a solution to what you've been talking about):

package main

import "fmt"

func tuple(args...) (...) { return args }

func main() {
a := tuple(1, "abc", 3.14);
fmt.Printf("%v %v %v\n", a);
}

To my surprise (=my fault - I should have realized that in advance),
it does compile and produces the correct output. Thank you for the
idea.


Qtvali

unread,
Dec 12, 2009, 5:10:13 PM12/12/09
to golang-nuts
Does this "func" there keep all function's variables as it points to
it's stack? In such case, lambda function tuples are to be deleted
asap when created if there are some arrays etc. in that function.

Russ Cox

unread,
Dec 12, 2009, 8:05:37 PM12/12/09
to befelemepeseveze, golang-nuts
> func tuple(args...) (...) { return args }

(Mentioned in another thread too.)

The compiler shouldn't accept this; it's not Go.
You can get the same effect from the
following legal function:

func tuple(args ...) interface{} { return args }

> Does this "func" there keep all function's variables as it points to
> it's stack? In such case, lambda function tuples are to be deleted
> asap when created if there are some arrays etc. in that function.

It doesn't point into the stack - keep it as long as you want.

Russ

Qtvali

unread,
Dec 12, 2009, 8:17:18 PM12/12/09
to golang-nuts
On Dec 12, 4:09 pm, Valentin Kasas <valentin.ka...@gmail.com> wrote:
> Is it a mistake to say that tuples are something like unnamed structs
> with unnamed fields only ?
> If not, you may consider using the ... argument to create some go
> equivalent to tuples.

z := tuple(a, b);
list(z) = 1, 5;

Should change the values of a and b. Tuple would be an union of
pointers. Using lists does not allow static typing of tuples - you
might know exactly, what your function takes in.

On the other hand:
z := tuple(a, int(0))
list(z) = 1, 5; // Changes value of a and integer value stored with
tuple

z := tuple(int(a), int(0))
list(z) = 1, 5; // Changes only values within tuple

Also, consider the call:
somefuntion(tuple(a, b, 3), tuple(a, b))

This would allow function to have something like two ...'s.

Also
z := tuple(function_call()) // Would not bind to variables inside
function, function will return normally

Also:
tuple(list(an_array)) // Would bind to all elements inside a list
This raises some questions about how to create tuple of unbind list.

array := { list(a_tuple) } // Would create an array of tuple contents

Steven Blenkinsop

unread,
Dec 13, 2009, 12:40:30 AM12/13/09
to golang-nuts
On Dec 12, 8:05 pm, Russ Cox <r...@golang.org> wrote:
> > func tuple(args...) (...) { return args }
>
> (Mentioned in another thread too.)
>
> The compiler shouldn't accept this; it's not Go.
> You can get the same effect from the
> following legal function:
>
> func tuple(args ...) interface{} { return args }
>
> Russ

Not quite.
func tuple1(args ...) (...) { return args }
func tuple2(args ...) interface{} { return args }

After passing a tuple1(<params>) to a function that accepts
(args ...), the type of args is struct{<param types>}
After passing a tuple2(<params>) to a function that accepts
(args ...), the type of args is struct{ struct{<param types>}}

Another note:
fmt.Println("Hello", tuple1("World")) : fails
fmt.Println("Hello", tuple2("World")) : prints "Hello {World}"

tuple2 seems better. However, I don't see the point, except to quickly
wrap data to send to a function that accepts interface{} types (and
then onerously unwraps it through reflection). Is there some
usefulness I'm missing?

befelemepeseveze

unread,
Dec 13, 2009, 4:18:45 AM12/13/09
to golang-nuts
On 13 pro, 02:05, Russ Cox <r...@golang.org> wrote:
> The compiler shouldn't accept this; it's not Go.

Good to know. Should it be then reported as a compiler bug? I browsed
the tracker and haven't found this one, but I'm not sure enough and I
don't want to introduce a duplicate.

Valentin Kasas

unread,
Dec 13, 2009, 5:31:50 AM12/13/09
to golang-nuts
> (Mentioned in another thread too.)
>
> The compiler shouldn't accept this; it's not Go.

But it's in the specs http://golang.org/doc/go_spec.html#Passing_arguments_to_..._parameters
:

"Within f, the ... parameter has static type interface{} (the empty
interface). For each call, its dynamic type is a structure whose
sequential fields are the trailing arguments of the call. That is, the
actual arguments provided for a ... parameter are wrapped into a
struct that is passed to the function instead of the actual
arguments."

> func tuple1(args ...) (...) { return args }
> func tuple2(args ...) interface{} { return args }

So the difference between tuple1 and tuple2 here is that tuple1
returns mutliple values while tuple2 returns its arguments wrapped
into a single struct value.

As long as a tuple is formally a single value with compound type, i'd
rather use tuple2 to create tuples.

befelemepeseveze

unread,
Dec 13, 2009, 6:03:23 AM12/13/09
to golang-nuts
On 13 pro, 11:31, Valentin Kasas <valentin.ka...@gmail.com> wrote:
> But it's in the specs  http://golang.org/doc/go_spec.html#Passing_arguments_to_..._parameters

> > func tuple1(args ...) (...) { return args }

IMHO the specs allows the ... construct iff appearing in ParameterDecl
(syntax production), not in the Result, so Russ's comment "it's not
Go" is correct.
See http://golang.org/doc/go_spec.html#Signature

Valentin Kasas

unread,
Dec 13, 2009, 8:30:14 AM12/13/09
to golang-nuts
> IMHO the specs allows the ... construct iff appearing in ParameterDecl
> (syntax production), not in the Result, so Russ's comment "it's not
> Go" is correct.
> Seehttp://golang.org/doc/go_spec.html#Signature


I'm afraid I do not agree. Look at the regexp definition of Result :

Result = Parameters | Type .

A result list is a parenthesized list of types whom last can be ... ,
(or an single unparenthesized non-function type ).

According to this, tuple1 signature is syntacticly correct.

befelemepeseveze

unread,
Dec 13, 2009, 9:09:41 AM12/13/09
to golang-nuts
On 13 pro, 14:30, Valentin Kasas <valentin.ka...@gmail.com> wrote:
> I'm afraid I do not agree. Look at the regexp definition of Result :
>
> Result         = Parameters | Type .
>
> A result list is a parenthesized list of types whom last can be ... ,
> (or an single unparenthesized non-function type ).
>
> According to this, tuple1 signature is syntacticly correct.
It _is_ syntactically correct, but IMO still not valid Go. The reason
I see is in the semantics
limitation of the syntax, that is explicitly stated in the paragraphs
below
http://golang.org/doc/go_spec.html#Signature
----
Within a list of _parameters_ _or_ _results_, the names .......... .

For the last _parameter_ _only_, instead of a type one may write ...
to indicate .......... .
----
This is to some extent analogous to a syntactically (only) valid
program with a reference to an undefined variable (unresolvable
semantics). That renders the whole thing invalid.

My 2c

Valentin Kasas

unread,
Dec 13, 2009, 9:11:25 AM12/13/09
to golang-nuts
> IMHO the specs allows the ... construct iff appearing in ParameterDecl
> (syntax production), not in the Result, so Russ's comment "it's not
> Go" is correct.
> Seehttp://golang.org/doc/go_spec.html#Signature


Russ Cox

unread,
Dec 13, 2009, 5:16:15 PM12/13/09
to befelemepeseveze, golang-nuts
Sure, please file a bug.

"..." is not a general type. It can only be used at
the end of a func's input parameter list. The bug
is that the compiler accepts it at the end of output
parameter lists too.

Russ

befelemepeseveze

unread,
Dec 14, 2009, 5:45:42 AM12/14/09
to golang-nuts
On 13 pro, 23:16, Russ Cox <r...@golang.org> wrote:
> Sure, please file a bug.
Here it is: http://code.google.com/p/go/issues/detail?id=423

bflm

Louki Sumirniy

unread,
Apr 19, 2018, 12:57:53 AM4/19/18
to golang-nuts
I think there is no really big reason to make a special tuple type for return values. The extra burden comes in specifying the type and wrapping the list in curly brackets, and predefining the elements in a type struct declaration. I am writing a dense binary tree implementation (dense as in not sparse like all previously invented BST's) and I have defined a 'cursor' structure which very conveniently can be used as both parameter and return value, and has made my code 1000x more readable and maintainable. Here is some snippets from this code:

type Cursor struct {
Index Index
Row   Row
Err   error
}

An example where I am just passing things through:

func (b *Bast) Parent(c Cursor) Cursor {
if c.Index == 0 {
c.Err = errors.New("No parent from the root")
return c
}
c.Row--
c.Index = c.Index>>1 - (c.Index+1)%2
c.Err = nil
return c
}

And here is an example where I named the variable in the function so it creates a new instance that I can work on separately to the input value:

func (b *Bast) Find(d Data) (c Cursor) {
for notfound := true; notfound && c.Err == nil; {
if b.Store[c.Index] == d {
return c
}
if b.IsLeft(d, c) {
c = b.LeftChild(c)
} else {
c = b.RightChild(c)
}
if c.IsOutside(b) || b.Store[c.Index].IsEmpty() {
c.Err = errors.New("Did not find data in tree")
}
}
return c
}

Jan Mercl

unread,
Apr 19, 2018, 1:31:01 AM4/19/18
to Louki Sumirniy, golang-nuts
On Thu, Apr 19, 2018 at 6:57 AM Louki Sumirniy <louki.sumir...@gmail.com> wrote:

func (b *Bast) Parent(c Cursor) Cursor {
if c.Index == 0 {
c.Err = errors.New("No parent from the root")
return c
}
c.Row--
c.Index = c.Index>>1 - (c.Index+1)%2
c.Err = nil
return c
}

This method looks like it should be defined be on *Cursor receiver, not *Bast.

--

-j

Louki Sumirniy

unread,
Apr 19, 2018, 8:36:28 AM4/19/18
to golang-nuts
Ok, it may look like it but I don't want to track the cursor in the datatype itself most especially for reasons being that even one of the 'sideways walk' functions in the library needs to keep a copy of the coordinate of the previous node iterated past in order to identify when the direction (left or right) changes, for specific (common) cases. The Bast is the data structure and contains all the data. Cursor is just a convenience way of storing the coordinate in the tree, the row of the coordinate and an easy way to return error values. So the 'cursor' has to be usable in external code, and external also to the data it points at, because there could be multiple trees being operated on within one application using this library.

Louki Sumirniy

unread,
Apr 19, 2018, 8:51:07 AM4/19/18
to golang-nuts
Just to clarify. Yes, the parent/child relationships are purely based on the indices of the nodes. It's not a conventional vector reference based binary tree. I was surprised when I came up with the idea that such did not even exist. Just to explain, it's a non-rectilinear array mapping. There is rows, and a root, and each row is double the size of the one above it. The name BAST comes from Bifurcation Array Search Tree. As far as I know it is the only non-linear/non-grid based ever invented. By me of course, so of course I am proud of it.

The coordinates stored in Cursor do not have any meaning except in a specific instance of the BAST array at a specific moment. I haven't worked out any strategies for multiple readers of the array, but I imagine it would just take a blocking table based on sections of the array, such as 4k pages or so, while the write process is changing it. Probably even, it's not possible to partially block read access for concurrency at all because many times write operations will stomp all over the tree in a random way because that's how hashes tend to be... random... Besides this, the atomic write operations generally won't block read access for long periods of time anyway. 

If I have piqued your curiosity, go check out the repo here: https://github.com/calibrae-project/bast - To sum it up in short words, it aims at eliminating retrieval latency caused by nonlinear seeking on memory devices. It has applicability to database lookup tables and should perform also quite well even on spinning disk cached stores (if they are allocated in one giant 2-4Gb blob).

Sorry for the self-promotion but it was relevant in that I was working on how to tidy up the readability of my code and needed multiple returns and simple untyped tuples were really not nearly as convenient as using a type struct. After doing a bunch of work on getting parametric polymorphism implemented in go, which is only obliquely relevant, I wrote a Gist about it: https://gist.github.com/l0k1verloren/469a4e467a2789daa04de60449af11cc No idea why nobody has written such a simple explanation and example of how to do it. You will spend 2 minutes on that and understand that actually, yes, you can do multiple inheritance in Go quite easily once you understand interface{} and parametric polymorphism is the simplest case.


On Thursday, 19 April 2018 08:31:01 UTC+3, Jan Mercl wrote:

Jan Mercl

unread,
Apr 19, 2018, 9:06:42 AM4/19/18
to Louki Sumirniy, golang-nuts
On Thu, Apr 19, 2018 at 2:51 PM Louki Sumirniy <louki.sumir...@gmail.com> wrote:

> Sorry for the self-promotion but it was relevant in that I was working on how to tidy up the readability of my code and needed multiple returns and simple untyped tuples were really not nearly as convenient as using a type struct.

I have no idea what you mean by 'untyped tuples' because Go does not have tuples, or at least not as a well defined thing. I can only guess if you're trying to implement tuples in Go with an array, slice or a struct, ...? To add to my confusion, Go functions can have as many return values as one wishes just fine, ie. I obviously do not even understand what problem you're trying to solve. Sorry.


--

-j

Louki Sumirniy

unread,
Apr 19, 2018, 12:03:55 PM4/19/18
to golang-nuts
Multiple return values. They do kinda exist in a declarative form of sorts, in the type signature, this sets the number and sequence and types of return values. You could even make functions accept them as also input values, I think, but I don't think it works exactly like this. I'm not a fan of these things because of how you have to nominate variables or _ and type inference will make these new variables, if you  := into whatever the return was.

I'm not sure what the correct word is for them. Untyped in the same way that literals can be multiple types (especially integers) but singular in their literal form.

Diogo Baeder

unread,
Dec 3, 2022, 10:47:03 PM12/3/22
to golang-nuts
Hi there, sorry for weighting in so late in the game, but I just started again to learn Go and was thinking why the language still doesn't have a tuple type.

Now, imagine this scenario: I have a web application which has to access a webservice that responds with JSON payloads; These payloads are a list of values, where each value is a smaller list like '[20220101, 1.234, "New York"]'. And these smaller lists follow the same type sequence: int64, float64, string. Suppose that I want to filter those values and send a response to the client, with the data structure unchanged (same format and types). Today, it doesn't seem to be possible to do that in Go, unless I do some dirty hack like decoding to '[]any' and then cast to the other types, and then hack again to put these values in the response to the client.

I totally understand the reasoning for preferring the usage of structs for heterogeneous data (and I myself do prefer them, they're much more powerful in general), but there's real world data that's available like in the example above, and we just can't go on changing them at their sources. I might be mistaken (please let me know if it's the case), but it seems like Go is missing an opportunity to interoperate with what's a fundamental data structure in many other languages (Python, Rust etc). I'm having a lot of fun learning to use the language, and would be happy to see this feature being implemented at the core.

(Maybe what I said above is total BS, I acknowledge that since I'm an almost complete ignorant in the language)

Cheers!

burak serdar

unread,
Dec 3, 2022, 11:34:16 PM12/3/22
to Diogo Baeder, golang-nuts
On Sat, Dec 3, 2022 at 8:47 PM Diogo Baeder <diogo...@gmail.com> wrote:
Hi there, sorry for weighting in so late in the game, but I just started again to learn Go and was thinking why the language still doesn't have a tuple type.

Now, imagine this scenario: I have a web application which has to access a webservice that responds with JSON payloads; These payloads are a list of values, where each value is a smaller list like '[20220101, 1.234, "New York"]'. And these smaller lists follow the same type sequence: int64, float64, string. Suppose that I want to filter those values and send a response to the client, with the data structure unchanged (same format and types). Today, it doesn't seem to be possible to do that in Go, unless I do some dirty hack like decoding to '[]any' and then cast to the other types, and then hack again to put these values in the response to the client.

What you described above is a struct. 
 

I totally understand the reasoning for preferring the usage of structs for heterogeneous data (and I myself do prefer them, they're much more powerful in general), but there's real world data that's available like in the example above, and we just can't go on changing them at their sources. I might be mistaken (please let me know if it's the case), but it seems like Go is missing an opportunity to interoperate with what's a fundamental data structure in many other languages (Python, Rust etc). I'm having a lot of fun learning to use the language, and would be happy to see this feature being implemented at the core.

In general, you can implement most tuple functionality using []any. However, when dealing with unpredictable data structures like an unknown JSON document, third-party libraries might be better suited than a map[string]any. When you have a tuple like that, you have to "discover" the type of each variable and parse it to do any nontrivial processing. You can't really unmarshal a string from a JSON document and expect to get a date in the tuple. My current work is on the interoperability of heterogeneous data, so for XML documents we use DOM libraries, for JSON documents we wrote https://github.com/bserdar/jsonom, etc.
 

(Maybe what I said above is total BS, I acknowledge that since I'm an almost complete ignorant in the language)

Cheers!

On Thursday, April 19, 2018 at 1:03:55 PM UTC-3 Louki Sumirniy wrote:
Multiple return values. They do kinda exist in a declarative form of sorts, in the type signature, this sets the number and sequence and types of return values. You could even make functions accept them as also input values, I think, but I don't think it works exactly like this. I'm not a fan of these things because of how you have to nominate variables or _ and type inference will make these new variables, if you  := into whatever the return was.

I'm not sure what the correct word is for them. Untyped in the same way that literals can be multiple types (especially integers) but singular in their literal form.


On Thursday, 19 April 2018 16:06:42 UTC+3, Jan Mercl wrote:
On Thu, Apr 19, 2018 at 2:51 PM Louki Sumirniy <louki.sumir...@gmail.com> wrote:

> Sorry for the self-promotion but it was relevant in that I was working on how to tidy up the readability of my code and needed multiple returns and simple untyped tuples were really not nearly as convenient as using a type struct.

I have no idea what you mean by 'untyped tuples' because Go does not have tuples, or at least not as a well defined thing. I can only guess if you're trying to implement tuples in Go with an array, slice or a struct, ...? To add to my confusion, Go functions can have as many return values as one wishes just fine, ie. I obviously do not even understand what problem you're trying to solve. Sorry.


--

-j

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/8e728e0f-341d-4340-a868-aac028dfc443n%40googlegroups.com.

Kevin Chowski

unread,
Dec 4, 2022, 9:39:41 PM12/4/22
to golang-nuts
If you really need anonymous tuple types that support decoding that sort of JSON, it isn't too hard to write one: https://go.dev/play/p/Fn_wUXh2drs

Go's generics don't support varargs types (...yet? who knows) so there'd be a little copypasta if you needed many different tuple lengths, but Java has been doing that for years ;)

(IMO, using these anonymous tuple types across a whole codebase is not great: data should be labeled with useful names as it is passed around a program. But if you really are just using this to make json parsing code easier, that seems reasonable to me.)

Diogo Baeder

unread,
Dec 5, 2022, 10:39:17 PM12/5/22
to golang-nuts
Hi folks,

Thanks for all the inputs, I really appreciate the effort and help :-)

Regardless of the whole discussion of whether or not tuples are a good or bad data structure, they exist out there in the wild, and we have to deal with them in many situations - of course, in our own code we can opt by continuing to use them or not, depending on the language we're using. It would be nice if Go could support them as first-class citizens - I know it's not the only language that doesn't, though, Java for example doesn't support them either (BTW there's a nice Java library, javatuples, which gives nice names like Pair, Triplet, Quartet etc, to n-sized "tuples" - I find that enjoyable).

Anyways, thanks for the discussion, that helps me get a better grasp of the language!

Cheers!

Robert Engels

unread,
Dec 5, 2022, 10:56:47 PM12/5/22
to Diogo Baeder, golang-nuts
I worked on a system one time - in Java - that used a Sextuplet. I kid you not. Please. Please. Please use typed structures and save the futures maintainers they pain. It’s a little extra work up front that pays big dividends. 

On Dec 5, 2022, at 9:39 PM, Diogo Baeder <diogo...@gmail.com> wrote:


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Roman Kuprov

unread,
Dec 6, 2022, 2:57:20 PM12/6/22
to golang-nuts
I might be missing something, but you can just iterate over your list of structs and delete/set-to-default-value the offending field before shipping it to your client. Same as tuples afaic.

Daniel Lepage

unread,
Dec 6, 2022, 5:27:38 PM12/6/22
to burak serdar, Diogo Baeder, golang-nuts
On Sat, Dec 3, 2022 at 11:34 PM burak serdar <bse...@computer.org> wrote:


On Sat, Dec 3, 2022 at 8:47 PM Diogo Baeder <diogo...@gmail.com> wrote:
Now, imagine this scenario: I have a web application which has to access a webservice that responds with JSON payloads; These payloads are a list of values, where each value is a smaller list like '[20220101, 1.234, "New York"]'. And these smaller lists follow the same type sequence: int64, float64, string. Suppose that I want to filter those values and send a response to the client, with the data structure unchanged (same format and types). Today, it doesn't seem to be possible to do that in Go, unless I do some dirty hack like decoding to '[]any' and then cast to the other types, and then hack again to put these values in the response to the client.

What you described above is a struct. 

I think the problem Diogo is pointing out is a reasonable one - you specify the type of the object you're unpacking by e.g. json.Unmarshal(data, &typedObject), but right now there's no way (AFAIK) for the typed object to capture "a list of [int, float, string] triples" except by capturing it as a [][]any and then manually typechecking that each one is indeed an int, a float, and a string.

It'd be great if, for example, you could tag an entire type like:

type Row struct { `json:tuple`
  date int64
  score float64
  city string
}

so that 

var v []Row
json.Unmarshal(data, &v)

would automatically parse the triples into usable structs (and json.Marshal would turn them back into lists).

*****

I also don't think arguments that Go doesn't need tuples really hold water, given that Go already *has* tuples, just only for return types. We could all be writing

func Foo() struct{int, error} {
   return struct{int, error}{3, nil}
}
v := Foo()
i, err := v.int, v.error
...

but I think everyone agrees that tuples make this code much more readable. Given that we all do agree that there are cases where having tuples makes for better code, I don't actually know why Go doesn't support them in general. Is it out of fear that people will use them poorly, or is it actually pretty complex to implement and not worthwhile?

-- 
Dan Lepage

Diogo Baeder

unread,
Dec 6, 2022, 10:54:40 PM12/6/22
to golang-nuts
That was perfectly put, Daniel, thanks! :-)

And, indeed, what I've been doing is to unmarshal as a "[][]any" and then coercing to the right types.

I also agree tuples have a place in the world. They're quite common when dealing with tabular data - for example, to reduce the amount of repetition when describing "rows" of data. But I don't really have to agree or disagree - they just exist, and that itself begs for an action: either ignore them, or find a workaround (like https://github.com/barweiss/go-tuple ), or implement the syntax, type etc for it (which I totally understand if it's too hard or undesirable).

Some approaches that I find interesting are Python's type hints (e.g. "tuple[int, float, str]", which is well checked with tools like mypy) or even the cleaner approach from Rust (e.g. "(i32, f64, String)"). But anyway, I digress.

I like the approach with struct tags too; While not being a real implementation of tuples, they at least help with unmarshalling and marshalling of data to cope with other existing services when they need to follow that structure.

Cheers!

Brian Candler

unread,
Dec 7, 2022, 6:16:53 AM12/7/22
to golang-nuts
On Tuesday, 6 December 2022 at 22:27:38 UTC dple...@google.com wrote:
It'd be great if, for example, you could tag an entire type like:

type Row struct { `json:tuple`
  date int64
  score float64
  city string
}

so that 

var v []Row
json.Unmarshal(data, &v)

would automatically parse the triples into usable structs (and json.Marshal would turn them back into lists).

You can wrap that pattern yourself though, and it's not too much work.

Diogo Baeder

unread,
Dec 7, 2022, 8:22:35 AM12/7/22
to golang-nuts
Thanks Brian, that's actually similar to what I'm already doing.

Cheers!

Thomas Bushnell BSG

unread,
Dec 7, 2022, 1:09:30 PM12/7/22
to Diogo Baeder, golang-nuts
Use the json package to parse the incoming data. Transform it as you please. Use the json package to format the output.

I'm not sure I see the problem.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages