i dare say: tuples should be in the core library

219 views
Skip to first unread message

Raoul Duke

unread,
Jul 7, 2014, 4:00:33 AM7/7/14
to haxe...@googlegroups.com
since there's now a zillion different implementations of them in
random hx libs it appears. :-{

Tarwin Stroh-Spijer

unread,
Jul 7, 2014, 2:08:19 PM7/7/14
to haxe...@googlegroups.com
I'd agree. It seems like this is something that other technologies are built upon. It really would make sense to support a "standard" of some kind. The only problem then is I'd say the same about signals, although it can be argued more that these are more framework tools.



Tarwin Stroh-Spijer
_______________________

phone: +1 650 842 0920

Developer at Fanplayr Inc. (Palo Alto)
Original at Touch My Pixel (touchmypixel.com)
_______________________


On Mon, Jul 7, 2014 at 1:00 AM, Raoul Duke <rao...@gmail.com> wrote:
since there's now a zillion different implementations of them in
random hx libs it appears. :-{

--
To post to this group haxe...@googlegroups.com
http://groups.google.com/group/haxelang?hl=en
---
You received this message because you are subscribed to the Google Groups "Haxe" group.
For more options, visit https://groups.google.com/d/optout.

Raoul Duke

unread,
Jul 7, 2014, 2:12:29 PM7/7/14
to haxe...@googlegroups.com
well we have to start sliding down the slope with *something*
;-)

Juraj Kirchheim

unread,
Jul 7, 2014, 4:40:45 PM7/7/14
to haxe...@googlegroups.com
You're grossly exaggerating, so I wonder what is the actual reason we
should add tuples to the core library?

While we're at it, why not also add a tweening engine and a game
engine and an entity component system and a web framework?

I think the std lib already greatly suffers from feature creep. I'd
prefer it to be smaller and better tested and more up to date with the
language itself. The stuff that's in there should be rock solid and
the rest doesn't belong there. It can take months until a patch you
landed actually is released. I don't find that acceptable.

I am all for supporting tuples at language level, pretty much the way
Scala does. I'm all against adding such trivial stuff to the standard
library. There could be a complementary foundation backed code base
(something like boost for C++) to deal with these issues. But *please*
not the std lib.

Best,
Juraj

Raoul Duke

unread,
Jul 7, 2014, 4:55:38 PM7/7/14
to haxe...@googlegroups.com
sure, whatever works, put it in the language! :-)

Franco Ponticelli

unread,
Jul 8, 2014, 11:24:42 AM7/8/14
to haxe...@googlegroups.com
+1


On Mon, Jul 7, 2014 at 10:00 AM, Raoul Duke <rao...@gmail.com> wrote:
since there's now a zillion different implementations of them in
random hx libs it appears. :-{

Heinz Hölzer

unread,
Jul 9, 2014, 4:22:44 PM7/9/14
to haxe...@googlegroups.com
+1 in conjunction with tuple unpacking like:

var (x,y) = returnTuple();

Tarwin Stroh-Spijer

unread,
Jul 9, 2014, 6:14:46 PM7/9/14
to haxe...@googlegroups.com
I don't quite understand why this is so useful when you can easily do something like:

---

function count(string: String) : {vowels: Int, consonants: Int, others: Int}
{
  return { vowels: 10, consonants: 17, others: 23 };
}
totals = count("some arbitrary string!") 
trace(totals.vowels);

---

Example from (Functions with Multiple Return Values):


---

Am I missing something big here? Is this not tuples? What do you really get?



Tarwin Stroh-Spijer
_______________________

phone: +1 650 842 0920

Developer at Fanplayr Inc. (Palo Alto)
Original at Touch My Pixel (touchmypixel.com)
_______________________


On Wed, Jul 9, 2014 at 1:22 PM, Heinz Hölzer <heinz....@googlemail.com> wrote:
+1 in conjunction with tuple unpacking like:

var (x,y) = returnTuple();

Cristian Baluta

unread,
Jul 10, 2014, 1:22:16 AM7/10/14
to haxe...@googlegroups.com
Probably they think it will be faster, which i don't think since no target supports tuples.
--

Cauê Waneck

unread,
Jul 10, 2014, 1:54:04 AM7/10/14
to haxe...@googlegroups.com
Actually all typed targets can greatly benefit from a tuple class instead of the dynamic anonymous type.
Also, there is a difference between tuples and anonymous types which make tuples more composable: tuples don't have field names.
Also since they are read-only we can probably generate a more optimized code - at least on C#.

Juraj Kirchheim

unread,
Jul 10, 2014, 4:58:12 AM7/10/14
to haxe...@googlegroups.com
Say `(A, B, ..., Z)` is the general notation for a tuple type, then in
particular `()` gives you void (which I think is quite elegant). And
every function can be represented as `A -> B`. Instead of
`Int->Int->Void` you write `(Int, Int) -> ()`.

The advantage of that is that for example you really need no `Signal0`
or `Signal2`. That's just `Signal<()>` and `Signal<(X, Y)>`.

However implementing this is not trivial. For one thing you want `(A,
(B, C), D)` to be `(A, B, C, D)` and stuff like that. Also ideally
tuples would come without overhead (compared to multiple
variables/arguments) whenever possible.

Best,
Juraj

Confidant

unread,
Jul 10, 2014, 8:24:41 AM7/10/14
to haxe...@googlegroups.com
That's what SHE said!

Raoul Duke

unread,
Jul 10, 2014, 1:12:34 PM7/10/14
to haxe...@googlegroups.com
> However implementing this is not trivial. For one thing you want `(A,
> (B, C), D)` to be `(A, B, C, D)` and stuff like that.

what?!

Raoul Duke

unread,
Jul 10, 2014, 1:16:08 PM7/10/14
to haxe...@googlegroups.com
ok well this is why i'm not a/the language designer i guess...

Prelude> 1 == 1
True

Prelude> (2) == (2)
True

Prelude> (2,3) == (2,3)
True

Prelude> (2,(3)) == (2,3)
True

Prelude> (2,(3)) == ((2),3)
True

Prelude> (2,(3)) == ((),3)
<interactive>:9:2:
No instance for (Num ()) arising from the literal `2'
Possible fix: add an instance declaration for (Num ())
In the expression: 2
In the first argument of `(==)', namely `(2, (3))'
In the expression: (2, (3)) == ((), 3)

Prelude> (2,(3)) == ((3),2)
False

Luca

unread,
Jul 10, 2014, 7:40:22 PM7/10/14
to haxe...@googlegroups.com
Raoul, Haskell has no syntax for creating a tuple of 1 element. (2) is just the same as 2. it is type Int not the unary tuple of one type Int. Haskell would most certainly never, ever, ever implicitly convert a unary tuple to the contained type or vice-versa, you just can't construct one to begin with :)

Luca

unread,
Jul 10, 2014, 7:41:07 PM7/10/14
to haxe...@googlegroups.com
and I agree with Raoul back2dos, wanting (A, (B, C), D) to be the same as (A, B, C, D) is just nonsense :)

Raoul Duke

unread,
Jul 10, 2014, 7:47:36 PM7/10/14
to haxe...@googlegroups.com
> and I agree with Raoul back2dos, wanting (A, (B, C), D) to be the same as
> (A, B, C, D) is just nonsense :)

but haskell does it, which yes was a surprise to me.

so does ocaml.

# (1) = (1);;
- : bool = true

# (1,2) = (1,2);;
- : bool = true

# (1,(2)) = ((1),2);;
- : bool = true

Luca

unread,
Jul 11, 2014, 3:30:12 AM7/11/14
to haxe...@googlegroups.com
Neither of them do it, read my post just above! (2) is not a tuple with 1 element, it is just a 2 in brackets. Haskell and OCaml do NOT equate the types (A, (B, C), D) and (A, B, C, D).

Juraj Kirchheim

unread,
Jul 11, 2014, 4:46:59 AM7/11/14
to haxe...@googlegroups.com
Your argument is just appeal to authority. Also a pretty lousy one if
we're really going to play a game of formalities. Haskell and OCaml
don't consider `(1, 2, 3)` and `(1, (2, 3))` equal although triples
are inductively defined in terms of pairs in just that way.

Anyway, I'd be more than happy to hear any actual reason why it's
nonsense. Until then, I maintain it's better to not have to write code
that deals with restructuring tuples. There is quite simply no point
in that, particularly since tuples do not convey meaning the way
structs do - or any type that actually gives things *names*. Yes, some
of the more masochistic programmers will love to do this:

typedef Point4 = (Float, Float, Float, Float)
typedef Rect = ((Float, Float), Float, Float)
typedef Segment = ((Float, Float), (Float, Float))
typedfe Ellipse = (Float, Float, (Float, Float))

And insist these types should be mutually incompatible. Well, I don't
care much for that. I favor usability and pragmatism over obscurity. I
want to be able to compose functions without worrying too much which
particular tuple structure is created. Having adjunction be
commutative (resulting in unordered pairs) probably causes more
problems than it solves, but I'm not convinced this is so for
associativity. Please do enlighten me on the downsides.

Best,
Juraj

Johann

unread,
Jul 11, 2014, 8:24:31 AM7/11/14
to haxe...@googlegroups.com
Assuming array access to get the nth element of a tuple, and .length to get the number of elements:

var t3 = (1,2,3);

function f<T>(v:T) return (1,2,v);
function f2<A,B>(a:A,b:B):(A,B) return (a,b);
function f3<A,B>(v:(A,B)) return v.length;

var a = (1,2,t3)[2];
var b = f(t3)[2];
var c = f3(f2(1,(2,3)));
var d = f3(f2(1,2));

What would be the types of a and b? Why should c be different from d and wouldn't the obvious return value of f3 be 2 in all cases? And so on and so on.

regards, Johann

Juraj Kirchheim

unread,
Jul 11, 2014, 9:54:26 AM7/11/14
to haxe...@googlegroups.com
Why would you want to access a tuple as an array? What will `(1,
2)[5]` do? And what's the point of reading the length, if the type
already defines it at compile time? Yes, you can define operations
that don't go together well with associative adjunction, but that can
be said about anything. The question should be: what adds the most
value?

What I was interested in is treating all functions as unary with the
help of tuples. Tuples behaving the way I suggest would give you that
for free. Your f2 would simply be the identity for pairs.

However, what I said about how Scala does it is actually only partly
correct, still, you can try this (http://scalatutorials.com/tour/):

def sum(x:(Int, Int)) = x._1 + x._2;
def sum2(x:Int, y:Int) = x + y;

sum((1, 2)) //> 3 //this actually works!
sum(1, 2) //> 3


val x = (4, 5) //> (4,5)
sum(x) //> 9
//sum2(x) //this doesn't ... go figure ...

What would probably work would be to just allow A->B->C to unify with
(A, B)->C and vice versa. That would already do the trick. You could
thus destructure pairs directly with functions. E.g.:

var keyValueList:Array<(Key, Value)> = ...;
keyValueList.map(function (key, value) { ... });

var s = new msignal.Signal<(String, String)>();
s.add(function (s1, s2) { ... });

Also with that you can easily expand the "arity" of a signal by
mapping with a function that does `(X, Y) -> (A, B, C)` or `(A, B, C)
-> D`. That is something I would call adding value, since there is
something to be said for composability.

I would argue that tuples as a reification of argument lists is really
a powerful thing. Say you have this:

class Base {
function foo(a:A, b:B, c:C, d:D, e:E):R { ... };
}

Then you can do that:

class Sub {
override function foo(p:(A, B, C, D, E)) {
trace('interrrrrrrrcepteeeeeeed');
return super.foo(p);
}
}

If tuples are added to the language, I think it would be nice for them
do something more than cause unnamed structures popping up all over
the place ;)

Best,
Juraj

Johann

unread,
Jul 11, 2014, 11:18:32 AM7/11/14
to haxe...@googlegroups.com
On Friday, July 11, 2014 3:54:26 PM UTC+2, back2dos wrote:
Why would you want to access a tuple as an array? What will `(1,
2)[5]` do? And what's the point of reading the length, if the type
already defines it at compile time? 
 
It's possible to show that the type (A,(B,C)) is not ((A,B),C) using whatever syntax to access the contents (or just by looking at them, but that obviously didn't work for you) so I used array style indexing to not introduce more made-up syntax into the example than necessary. And I used the .length as a vehicle to point out the runtime issues that your idea of "tuples" would introduce in the presence of type erasure. And I'm so glad you're asking, giving me the opportunity to clarify this immensely relevant point.


What I was interested in is treating all functions as unary with the
help of tuples. Tuples behaving the way I suggest would give you that
for free. 


So it turns out that what you want has nothing to do with tuples to start with. Tuples are product types, and (A,A,A) simply doesn't unify with (A,(A,A)). 

I don't agree that what you have in mind would come for free. Despite having a certain appeal, it would have quite an impact by complicating the implementation of calls significantly, introduce issues with interfacing platform native code and whatnot. 

regards, Johann

Juraj Kirchheim

unread,
Jul 11, 2014, 1:52:38 PM7/11/14
to haxe...@googlegroups.com
On Fri, Jul 11, 2014 at 5:18 PM, Johann <johann...@densedata.com> wrote:
>
> On Friday, July 11, 2014 3:54:26 PM UTC+2, back2dos wrote:
>>
>> Why would you want to access a tuple as an array? What will `(1,
>> 2)[5]` do? And what's the point of reading the length, if the type
>> already defines it at compile time?
>
> It's possible to show that the type (A,(B,C)) is not ((A,B),C) using
> whatever syntax to access the contents (or just by looking at them, but that
> obviously didn't work for you) so I used array style indexing to not
> introduce more made-up syntax into the example than necessary.

That depends entirely on how you define such a type. You're just
implicitly assuming that the tuple definitions you seem to be used to
is the only possible one.

> And I used the .length as a vehicle to point out the runtime issues that your idea of
> "tuples" would introduce in the presence of type erasure. And I'm so glad
> you're asking, giving me the opportunity to clarify this immensely relevant
> point.

I am asking because I see no practical application here. This was the
problem you were able to come up with and my answer is that it's
quixotic. If you wish to discuss more relevant points, by all means,
make them.

>> What I was interested in is treating all functions as unary with the
>> help of tuples. Tuples behaving the way I suggest would give you that
>> for free.
>
> So it turns out that what you want has nothing to do with tuples to start
> with.

Yes, tuples can be defined independently of what I would like to
achieve. The reverse however, what I want being independent of (or as
you would say "having nothing to do with") tuples, is neither
generally nor particularly true. And since I assume that you are
familiar with basic formal logic, I find your "conclusion" quite
disingenuous.

> Tuples are product types, and (A,A,A) simply doesn't unify with (A,(A,A)).

That is how you choose to define them. On the other hand formally `(A,
A, A)` is defined precisely as `(A, (A, A))`, as explained here:
http://en.wikipedia.org/wiki/Tuple#Tuples_as_nested_ordered_pairs
Which is also how they are composed in Lisp. I will concede that it's
uncommon to have `((A, A), A)` and `(A, (A, A))` be equivalent, which
is what I think you've been getting at. But that does not mean that
defining tuples with such an equivalency will make the universe
explode.

You can insist on your definition of tuples and then by circular
argumentation and repeated insistence dismiss any other ideas, but I
really don't find that helpful. In the end it doesn't matter how
academics define them, or how Haskell or Scala or Lisp or
whoever/whatever defines them. What matters is having a definition
that proves practically useful. That is what we should discuss and I
would prefer that a lot to being presented arbitrary axiomatic choices
as fact.

> I don't agree that what you have in mind would come for free.

You are free to disagree. Better yet, if you are challenging a
universal statement I have made, you're in the advantageous position
of being able to prove your counterclaim with a simple example, that I
would be very interested to see ;)

> Despite having a certain appeal, it would have quite an impact by complicating the
> implementation of calls significantly, introduce issues with interfacing
> platform native code and whatnot.

That is speculation. Now you are making universal statements and so I
shall give you a counter example. Allowing unification between n-ary
functions and unary tupled functions by means of wrapping through the
implict conversions (the same that abstracts allow for) would actually
not be hard. It would change *nothing* about the implementation of
calls. It would have no effect about interfacing with platform native
code. A runnable proof of concept can be seen here:
http://try.haxe.org/#85A97

And I am not even saying this is the best solution. It's the first one
I come up with. And I for one would like to believe that if you
gentlemen stopped mechanically talking back and put your brains into
thinking about powerful applications of tuples if we integrate them
with other language features more deeply, we could come up with
something vastly more powerful than unnamed structs, which by
themselves tend to just invite an obscure coding style.

As for different nesting: I am not convinced that allowing an
analogous implicit conversion from `((A, B), C)` to `(A, (B, C))` is a
bad idea. Yes, they can have different runtime representations, but no
real argument has yet been made why having the compiler generate the
transformation from one structure to another for you is a bad bad
thing.

Best,
Juraj
Reply all
Reply to author
Forward
0 new messages