Implicit Cast Function Receiving Tuple

Implicit cast function receiving tuple

Before Swift 3, one was able to call a function either by explicitly specifying its arguments, or by passing a well-crafted tuple. However this way of calling functions was removed when SE-0029 was implemented.

Basically, the following was possible:

func buildDescription(name: String, age: Int) -> String {
return "Hi, I am \(name), and I am \(age)"
}

buildDescription("John Doe", age: 21)
// or, via a tuple, giving the same result
buildDescription(("John Doe", name: 21))

The Swift forums have this post regarding the above change (emphasis mine):

The proposal has been accepted for Swift 3. We acknowledge that we're removing a useful feature without providing an equally expressive drop-in replacement. However, maintaining this behavior in the type checker is a severe source of implementation complexity, and actively interferes with our plans to solidify the type system.

So it looks like the call-function-by-tuple support was prohibited only at the type checker level, meaning you cannot directly pass tuples to functions, however the internals of the compiler remained the same, which allow indirect passes of tuples, like in the examples from the question.

Cannot implicitly convert deconstructable type to Tuple

There's no implicit conversion to ValueTuple, and your assignment is not invoking the deconstruction. You're essentially attempting to do the following which is not legal:

xy = (ValueTuple<int, int>)size;

You need two variables, not one. This is made more obvious if you consider that deconstruction is just compiler trickery/syntactic sugar for invoking the Deconstruct method. If you were going to call it manually, you'd need to pass two variables as out parameters, not one.

int x, y;
size.Deconstruct(out x, out y);

There's not an overload that takes a single tuple. How would that work? There's only a single variable. I suppose you might be thinking that the compiler could do something akin to:

size.Deconstruct(out xy.Item1, out xy.Item2);

Unfortunately it doesn't. For your case, you'll need to declare the variables seperately (not as a ValueTuple) and then use deconstruction and tuple-syntax to assign to them. If you wanted, you could move the assignment from the default case out of the switch to give the declaration of the two variables a more tuple-y feel:

var (x, y) = (0, 0); // two variables 
switch (deconstructableStruct)
{
case Size size:
(x, y) = size;
break;
case Point point:
(x, y) = point;
break;
}

You can still switch on the values later, you just need to use tuple syntax:

return (x, y) switch {
(0, 0) => "Empty",
(0, _) => "Extremely narrow",
(_, 0) => "Extremely wide",
_ => "Normal"
};

See this answer for a good alternative: writing your own implicit conversion operator if you really need a variable that is of a tuple-type on it own (and you are both able to and don't mind modifying the Size and Point types to add this behavior).

generate an implicit tuple while calling a function

If the notation

Command(mode, addr)(neighbour0, neighbour1);

is acceptable, Command() could return essentially a function object with a bound first std::tuple<...> which would call the actual function when receiving the other arguments. That is the implementation would be something along the lines of

template <typename... Out, typename... In>
void realCommand(std::tuple<Out...>, std::tuple<In...>);

template <typename... Out>
auto Command(Out&&... out) {
return [&](auto&&... in){
realCommand(std::make_tuple(std::forward<Out>(out)...),
std::make_tuple(std::forward<decltype(in)>(in)...));
}
}

C#7 value tuple/deconstruction asymmetry

The ability to have deconstructs act like implicit converters was something that was asked for (by me, so I'm biased here) before C# 7 was released. The response from the team was (as I read it anyway) that it was asked for too near to the C# 7 release and would have taken too long to implement, so wasn't up for consideration. As it would now be a breaking change, it's not something that will ever happen.

Please see the "Allow Deconstruct and implicit operator to both support deconstruction and conversion to tuple types" roslyn repo issue for the actual discussion on the matter.

Extending type conversion to pairs/tuples of convertable types

You cannot make implicit conversion to an unknown type; and again, conversion operator must be a non-static member function which will still require you to wrap it a class; and write a converting constructor from an unknown type (aka templated constructor).

Why don't you want to make it a free function:

template<typename To, typename From>
std::pair<To, To> convert(const std::pair<From, From>& p){
return std::make_pair( static_cast< To >( p.first ),
static_cast< To >( p.second ) );
}

And then call it like:

std::pair<float, float> mp{3.424, 59.35};
auto p2 = convert<double>(mp);

That's just about as clear as it can be. See it Live on Coliru

EDIT
(As per OP's comment):

You could do a quick Type for that sort of thing:

template<typename T>
class Point{
public:
T x = T{};
T y = T{};

template<typename Y>
Point(Point<Y> p) :
x(static_cast<T>(p.x)),
y(static_cast<T>(p.y))
{ }

Point(T x_val, T y_val) : x(x_val), y(y_val)
{ }

Point(Point&&) = default;
Point(const Point&) = default;
Point& operator = (Point&&) = default;
Point& operator = (const Point&) = default;
};

Consider this function that uses a double as the type parameter to Point:

void print(Point<double> p){
std::cout << "(" << p.x << ", " << p.y << ")\n";
}

The statements below will all work because of the Converting constructor.

int main() {
Point<float> mp{4.535, 395.3};
Point<int> ip = mp;
print(mp);
print(ip);
return 0;
}

See it Live on Coliru

define a function with tuples

There is no trait specifically for tuples, but you could use a typeclass approach, as demonstrated in this answer.

If your goal is really to have a List but allow callers to pass in tuples (for convenience), you can modify that solution so that the type class produces a List rather than a Product.

In brief, the idea is that you provide implicit conversions from the types that callers can pass to the type you're actually going to use:

def foo(x: IndexList) = x.indices

sealed case class IndexList(indices: List[Int])

object IndexList {
implicit def val2indices(i: Int) = IndexList(List(i))
implicit def tuple2toIndices(t: (Int, Int)): IndexList =
product2indices(t)
// etc
implicit def list2indices(l: List[Int]) = IndexList(l)

private def product2indices(p: Product) =
IndexList(p.productIterator.toList.asInstanceOf[List[Int]])
}

You can then call your method with any type for which you've provided a conversion:

foo(1)
foo((2,3))
foo(List(1,2,3))


Related Topics



Leave a reply



Submit