DEV Community

Cover image for Tuples are ok

Tuples are ok

Pragmatic Maciej on March 28, 2020

There are opinions in the community that Tuple types should not be used ever. There are movements also against functions arguments, and using one d...
Collapse
 
aminnairi profile image
Amin

Too bad JavaScript does not have a real Tuple system compared to Elm.

numbersToPair : Int -> Int -> ( Int, Int )
numbersToPair number1 number2 =
    ( number1, number2 )


doSomething : Int -> Int -> ( Int, Int, Int )
doSomething number1 number2 =
    let
        ( first, second, third ) = numbersToPair 1 2

    in
        ( first, second, third ) 
Enter fullscreen mode Exit fullscreen mode

This code will produce

This definition is causing issues:

13|>    let
14|>        ( first, second, third ) = numbersToPair 1 2
15|>
16|>    in
17|>        ( first, second, third ) 

This `numbersToPair` call produces:

    ( Int, Int )

But then trying to destructure it as:

    ( a, b, c )
Enter fullscreen mode Exit fullscreen mode

While this code in JavaScript

"use strict";

const numbersToPair = (number1, number2) {
    return [number1, number2];
}

const doSomething = (number1, number2) {
    const [first, second, third] = numbersToPair(number1, number2);

    return [first, second, third];
}
Enter fullscreen mode Exit fullscreen mode

Will work, and third will get undefined. I guess JavaScript is gone on a path where Tuple is something that will never see the light anytime soon. And I'm really sad to see that.

Collapse
 
macsikora profile image
Pragmatic Maciej

I don't think it is fair comparison. JS has no compiler so such construct you have put here has no place to be verified, and because of weak typing JS even don't throw here runtime error.

But its true that in JS there is nothing like tuple at the language level, its just an Array with fixed length, nothing more. So it is effected by all array methods like push or pop which never should be in tuple type.

We can though compare Elm with TypeScript, and here if you will type it correctly the error also will be there. Consider:

const numbersToPair = (number1: number, number2: number): [number, number] => {
    return [number1, number2];
}
const doSomething = (number1: number, number2: number) => {
    const [first, second, third] = numbersToPair(number1, number2); // error
    return [first, second, third];
}
Enter fullscreen mode Exit fullscreen mode

The Playground

TypeScript correctly shows an error here 👌.

BTW. I am also fan of Elm. So thanks for the comment!

Collapse
 
aminnairi profile image
Amin

I didn't know that we could use that Tuple-like syntax in TypeScript. thanks for showing. There are places where it can be very useful.

But since TypeScript will eventually transpile to JavaScript (and since we can't force users of our libraries to use TypeScript) there will always be a flaw in that system, where in Elm you have to use Elm.

Hence why I compared Elm and JavaScript because TypeScript is in the end a superset, not a language (unfortunately).

Thread Thread
 
macsikora profile image
Pragmatic Maciej • Edited

TypeScript is a language. It has no run-time representation, types are removed, but you cannot say its not an language only because it a superset of another language. You can also have run-time data decoders and validators via Elm by using TS abstraction, at the end of the day both languages land into JS.

Thread Thread
 
aminnairi profile image
Amin

Yes you are correct. I was wrong for saying that this is not a language.

My point was to say that you can have interfaces, tuples, and everything you want, this won't prevent me from using your library, compile it to javascript and use the wrong types because in the runtime, it is JavaScript.

When with Elm, even when you are using ports you get type safety because even if Elm does compile to javascript, you are in the sandbox of the Elm runtime, which keeps you safe in terms of type checking. And if you dig in the documentation, you'll see that all side effects are handled by the Elm runtime so that you never have to leave the runtime for anything (except again using ports).

Thread Thread
 
cherrydt profile image
David Trapp

Well you can also link an assembly program to a C++ library and call a function of the library (by its mangled name) with all the wrong calling convention and argument types and have the library code crash on the call. Yet C++ is a language...

Collapse
 
lionelrowe profile image
lionel-rowe • Edited

You can now use labeled tuples, available since TypeScript 4.0!

Really useful for APIs with a standard ordering of defined arguments, such as HTML5 canvas's handling of rects:

type Rect = [x: number, y: number, width: number, height: number]

const canvas = document.querySelector('#my-canvas')
const ctx = canvas.getContext('2d')

const img = document.querySelector('#my-semitransparent-image')
const { width, height } = img

const rect: Rect = [0, 0, width, height]

ctx.fillStyle = '#f00'
ctx.fillRect(...rect)
ctx.drawImage(img, ...rect)
Enter fullscreen mode Exit fullscreen mode