Language features needed to add "map" for heterogeneous tuples?


#1

I got this crazy idea that it would be nice to be able to write something like this:

fn foo(inp: (Option<String>, Option<i32>)) -> (String, i32) {
    inp.map_into(|x| x.unwrap())
}

My understanding is that to write map_into, we would need to extend higher-rank trait bounds to type variables (not just lifetimes):

impl<A, B> (A, B) {
    fn map_into<F>(self, f: F) -> (?, ?, ?)
        where F: for<T, U> Fn(T) -> U>
    {
        let (a, b) = self;
        (f(a), f(b))
    }
}

But even then, I can’t figure out how one would hypothetically write the return type of map_into. It seems like this is an unfortunate limitation of HRTB. Is there a feature in GHC-flavored Haskell that plays nicely with RankNTypes that one could look to for inspiration? Is this a legitimate use-case for true return type deduction a la C++14, or C++11-style decltype? Does it even make sense to use the U parameter in that way (merely as an “output” type)?

In C++14 with auto return type and generic lambdas (which I think are higher-rank) one can write this, which more or less does the same thing (ignoring moves/copies):

#include <tuple>
#include <string>
#include <vector>

#include <iostream>

template <typename A, typename B, typename F>
auto map_into(std::tuple<A, B> inp, F f) {
    return std::make_tuple(f(std::get<0>(inp)), f(std::get<1>(inp)));
}

int main()
{
    auto foo = std::make_tuple(std::string { "foobar" }, std::vector<int> {});
    auto bar = map_into(foo, [](auto x) {
        // could even be distinct return types, e.g. returning x.cbegin()
        return x.size();
    });
    std::cout << std::get<0>(bar) << " " << std::get<1>(bar) << std::endl;
}

#2

Rust’s type-system can already define map_into (http://is.gd/jJrSah), but you can’t create polymorphic closures directly - currently you must implement them manually.


#3

Huh, that’s very cool. I didn’t realize that bounding by several FnMut traits was practical but it makes sense. Thanks for the enlightening reply!

(I’m looking forward to polymorphic closures in future Rust, perhaps :-))