Is There a Name for This Tuple-Creation Idiom

Is there a name for this tuple-creation idiom?

I think this is a subtle implementation of a Monad-like thing, specifically something in the same spirit of the continuation monad.

Monads are a functional programming construction used to simulate state between different steps of a computation (Remember that a functional language is stateless).

What a monad does is to chain different functions, creating a "computation pipeline" where each step knows about the current state of the computation.

Monads have two primary pilars:

  • A return function, which takes a value and returns it in a Monad-ready form.
  • A bind function, which takes a Monad-ready value (From the previous pipeline step) and unwraps it to its original from to pass the value to the next step.

The Wikipedia has very good examples and explanations about monads.

Let me rewrite the given C++14 code:

auto list = []( auto... xs ) 
{
return [=]( auto access ) { return access(xs...); };
};

I think here we identify the return function of a monad: Takes the value and returns it in a Monadic way.
Specifically, this return returns a functor (In the mathematical sense, not a C++ functor) which goes from the "tuple" category to the variadic pack category.

auto pack_size = [](auto... xs ) { return sizeof...(xs); };

pack_size is just a normal function. It would be used in a pipeline to do some work.

auto bind = []( auto xs , auto op ) 
{
return xs(op);
};

And length is only a non-generic version of something near to the monad bind operator, an operator which takes a monadic value from a previous pipeline step, and bypasses it to the specified function (Function which really does the work). That function is the functionality done by this computation step.

Finally your call could be rewritten as:

auto result = bind(list(1,'2',"3"), pack_size);

So, whats the name of this tuple creation idiom? Well, I think this could be called "monad-like tuples", since its not exactly a monad, but the tuple representation and expansion works in a similar way, remaining to the Haskell continuation monad.

Edit: More fun

Just for the shake of funny C++ programming, I have followed exploring this monad-like thing. You could find some examples here.

Is there a name for this C++ idiom in which a type vends a wrapper that expands its interface?

Your intent is unfortunately not fully clear to me.

At first, I thought that you'd just reinvented the decorator pattern, in which you add dynamically some responsibilities (accessor) to an existing object.

But looking more closely, I think that it all just looks like an anti-pattern, a dependency mess, and a flawed design: eveytime that in the base class you need to be friend of derived class, you should have alarm bells starting to ring.

Instead of looking for a name, you'd better engineer a cleaner design. Forget the enum, and go for an abstract Phone base class, with abstract functions corresponding to what every phone shall be able to do. Then create two derived concrete classes: LandLine and CellPhone, both inheriting from Phone.

Now you could consider that getting the list of apps is a common functionality of all kind of phones, and that a LandLine just returns an empty list. All your code would then just use build in polymorphism to do the job in a proper an extensible way:

  • If tomorrow, someone would invent a TelepathyPhone, you'd just need to impelement the common functions required by the abstract interface, and all the using code would still work unchanged.
  • In the worst case, if you'd really need to call a very specific class dependent function that was totally unknown in the common interface (e.g. TelepathyPhone::displayBrainWavelength()), you could go for an if using dynamic_cast. At least you'd avoid to create a new enum everytime you invent a new derived class.

What are named tuples in Python?

Named tuples are basically easy-to-create, lightweight object types. Named tuple instances can be referenced using object-like variable dereferencing or the standard tuple syntax. They can be used similarly to struct or other common record types, except that they are immutable. They were added in Python 2.6 and Python 3.0, although there is a recipe for implementation in Python 2.4.

For example, it is common to represent a point as a tuple (x, y). This leads to code like the following:

pt1 = (1.0, 5.0)
pt2 = (2.5, 1.5)

from math import sqrt
line_length = sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)

Using a named tuple it becomes more readable:

from collections import namedtuple
Point = namedtuple('Point', 'x y')
pt1 = Point(1.0, 5.0)
pt2 = Point(2.5, 1.5)

from math import sqrt
line_length = sqrt((pt1.x-pt2.x)**2 + (pt1.y-pt2.y)**2)

However, named tuples are still backwards compatible with normal tuples, so the following will still work:

Point = namedtuple('Point', 'x y')
pt1 = Point(1.0, 5.0)
pt2 = Point(2.5, 1.5)

from math import sqrt
# use index referencing
line_length = sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)
# use tuple unpacking
x1, y1 = pt1

Thus, you should use named tuples instead of tuples anywhere you think object notation will make your code more pythonic and more easily readable. I personally have started using them to represent very simple value types, particularly when passing them as parameters to functions. It makes the functions more readable, without seeing the context of the tuple packing.

Furthermore, you can also replace ordinary immutable classes that have no functions, only fields with them. You can even use your named tuple types as base classes:

class Point(namedtuple('Point', 'x y')):
[...]

However, as with tuples, attributes in named tuples are immutable:

>>> Point = namedtuple('Point', 'x y')
>>> pt1 = Point(1.0, 5.0)
>>> pt1.x = 2.0
AttributeError: can't set attribute

If you want to be able change the values, you need another type. There is a handy recipe for mutable recordtypes which allow you to set new values to attributes.

>>> from rcdtype import *
>>> Point = recordtype('Point', 'x y')
>>> pt1 = Point(1.0, 5.0)
>>> pt1 = Point(1.0, 5.0)
>>> pt1.x = 2.0
>>> print(pt1[0])
2.0

I am not aware of any form of "named list" that lets you add new fields, however. You may just want to use a dictionary in this situation. Named tuples can be converted to dictionaries using pt1._asdict() which returns {'x': 1.0, 'y': 5.0} and can be operated upon with all the usual dictionary functions.

As already noted, you should check the documentation for more information from which these examples were constructed.

Idiom for long tuple unpacking

Anwering your question "Which of the three options is the best?"

pep8 states:

The preferred way of wrapping long lines is by using Python's implied line continuation inside parentheses, brackets and braces. Long lines can be broken over multiple lines by wrapping expressions in parentheses. These should be used in preference to using a backslash for line continuation.

This Means the second one is preferred over the first one. The third one is fine conforming pep8 as well, though personally wouldn't recommend it.

Continue with the continuation monad tuple. Whats wrong?

A simple case that reproduces your problem:

void f(int) {}
void f(double) {}

template<class T> void call_with_3( T t ) { t(3); }

int main() {
call_with_3( f );
}

Here we can see that which f to call cannot be determined at the point where we pass it to call_with_3. Now, you seemingly don't have multiple overloads (you only have one f!), but...

A template is not an instance. A template function is a factory of functions, not a function.

There is no object or value there to pass around.

When you pass a function name as an argument, overload resolution kicks in. If the target type is known (as a function reference or pointer) it is used to do overload resolution on the function name.

In this case, you are passing a function name to a template (auto argument), so there is no overload resolution that can be done, so no particular value can be found, so you get an error.

You can create an object whose effect is to do overload resolution on the invoked arguments with a given function name. I call them overload set objects.

static struct f_overload_set_t {
template<class... Args>
auto operator()(Args&&... args) const {
return f(std::forward<Args>(args)...);
}
} f_overload_set;

in C++11 you need a ->decltype( f( std::declval<Args>()... ) ) after the const.

Now f_overload_set(blah) will, when invoked will (almost) do what happens when you f(blah), but f_overload_set is an actual object. So you can pass it around.

Macros that generate such overload sets are relatively easy to write. They can also use lambdas, as the above is a lot like a stateless lambda if you think about it.

The nice thing about the stateless lambda based macro overload set generator is that it can be created at point-of-use. From @dyp's comment above:

#define OVERLOAD_SET( FUNC )\
([](auto&&... args){\
return FUNC(std::forward<decltype(args)>(args)...);\
})

(note: no brackets around FUNC, as that blocks ADL). Brackets around everything else, because otherwise if used within a subscript operation (operator[]), it would be parsed as a [[ starting an attribute, among other spots (thanks to @ecatmur))

which makes your code:

template<typename... ARGS>
void f( const std::tuple<ARGS...>& t ){}

int main() {
tuple(1,2,3)(std_tuple)(OVERLOAD_SET(f));
}

List vs tuple, when to use each?

There's a strong culture of tuples being for heterogeneous collections, similar to what you'd use structs for in C, and lists being for homogeneous collections, similar to what you'd use arrays for. But I've never quite squared this with the mutability issue mentioned in the other answers. Mutability has teeth to it (you actually can't change a tuple), while homogeneity is not enforced, and so seems to be a much less interesting distinction.

Meaning of leading underscore in list of tuples used to define choice fields?

The leading underscore is the commonly used function alias for the one of the ugettext functions used by the internationalization (i18n) mechanics.

It means that when you have i18n running, the choicefield labels will be translated into the appropriate end-user language, if a translation is available.

At the top of a file that features this kind of syntax, you should see (or if not, you should have) something like:

from django.utils.translation import ugettext_lazy as _

See the docs here for more details



Related Topics



Leave a reply



Submit