c++parsingcompiler-constructionoperator-keywordlanguage-design

Unpack / pack operator


I'm looking to find various ways that a packing / unpacking operator is implemented. As an example:

*[1,2,3]    --> 1,2,3     (one array scalar value unpacked to three values)
*1,2,3      --> [1,2,3]   (three values packed to one scalar array value)

Is this a common operator in languages? If so, how is it usually represented?

Other possible terms:


Solution

  • You mention array, but packing and unpacking just mean, respectively, making 1 bundle out of multiple entities before passing the whole bundle to a function, and passing the constituents of a bundle as separate arguments to a function.

    What the bundle actually is (an array, a tuple, a struct) is kind of an implementation detail, as long as you have a way to create one out of the constituents, and a way to pull the constituents out of one.

    In C++, a std::tuple is preferable, because that gives you freedom on both the number and the types of entities you can bundle together. A std::array would restrict you to use the same type for all entities, so it's out of question in most scenarii.

    So when you want to pack together more entities before passing them to a function, you just use std::make_tuple.

    For unpacking, the situation is more complex, but luckily there's some ready-to-use library solution: Boost.Hana offers boost::hana::unpack, which allows you to do something like this

    constexpr auto add = [](auto x, auto y, auto z) {
        return x + y + z;
    };
    std::cout << hana::unpack(hana::make_tuple(1, 2, 3), add); // prints 6
    

    (The example uses boost::hana::tuple, but it's easy to extend Hana to make hana::unpack work for std::tuples as well.)

    In this respect, std::tie and structured bindings are kind of the minimal building blocks, because they allow you to give names to the individual entities before you pass them to the desired function. With reference to the example above, you could rewrite it as

    constexpr auto add = [](auto x, auto y, auto z) {
        return x + y + z;
    };
    auto const& [a, b, c] = std::make_tuple(1, 2, 3);
    std::cout << add(a, b, c); // prints 6
    

    which is bit more cumbersome, because you have to invent name that might not actually make much sense, depending on the specific case, clearly; as much clearly, if the tuple is just a collection of unrelated things which will take unrelated code paths, then maybe structured binding makes more sense.


    In other languages packing and unpacking is more straightforward. Take JavaScript, where arrays can be etherogenous; packing more things just means enclosing them in a ,-separated list inside [ and ]. For unpacking, there's the ... operator. Here's native JavaScript code that corresponds to the C++ code above which uses a full-fledged library:

    add = x => x[0] + x[1] + x[2]
    console.log(add([1, 2, 3])) // prints 6
    add = (x, y, z) => x + y + z;
    console.log(add(...[1, 2, 3])) // prints 6


    Finally, in a language like Haskell, where functions are all curried, i.e. they accept arguments one by one, packing and unpacking fundamentally coincide with uncurrying and currying respectively:

    add3'curried = \x y z -> x + y + z -- === curry3 add3'uncurried
    triple = (1, 2, 3)
    uncurry3 add3'curried $ triple -- prints 6
    
    add3'uncurried = \(x, y, z) -> x + y + z -- === uncurry3 add3'curried
    curry3 add3'uncurried 1 2 3 -- prints 6