markable -- informal review request
Hi Everyone, I would like to ask your opinion, suggestions, and perhaps an informal review of the 'markable' library. An alternative to boost::optional when performance really matters. It is the second incarnation of the 'compact_optional' library that I have presented last year. The main differences from the previous version: * name change: it is called 'markable' so that it is not confused with 'optional'. It uses "mark values" and mark policies" * it has the ability to mark an empty state even for the types with strong invariants, by marking a bit pattern rather than any proper value of T (an idea by Matt Calabrese) An example use case: typedef markable<mark_int<int, -1>> opt_int; opt_int oi; opt_int o2 (2); assert (!oi.has_value());assert (o2.has_value());assert (o2.value() == 2); static_assert (sizeof(opt_int) == sizeof(int), ""); The github repository: https://github.com/akrzemi1/markable And documentation therein: https://github.com/akrzemi1/markable/tree/master/documentation Any comments, suggestions or critique are most welcome. Regards, &rzej;
On 16 September 2016 at 15:23, Andrzej Krzemienski <akrzemi1@gmail.com> wrote:
Hi Everyone, I would like to ask your opinion, suggestions, and perhaps an informal review of the 'markable' library. An alternative to boost::optional when performance really matters. It is the second incarnation of the 'compact_optional' library that I have presented last year.
The main differences from the previous version: * name change: it is called 'markable' so that it is not confused with 'optional'. It uses "mark values" and mark policies" * it has the ability to mark an empty state even for the types with strong invariants, by marking a bit pattern rather than any proper value of T (an idea by Matt Calabrese)
An example use case:
typedef markable<mark_int<int, -1>> opt_int;
opt_int oi; opt_int o2 (2); assert (!oi.has_value());assert (o2.has_value());assert (o2.value() == 2); static_assert (sizeof(opt_int) == sizeof(int), "");
The github repository: https://github.com/akrzemi1/markable And documentation therein: https://github.com/akrzemi1/markable/tree/master/documentation
Any comments, suggestions or critique are most welcome.
Regards, &rzej;
I am interested in doing a quick review of this library, but I have first to ask why "markable"? I'm not sure I understand the intended meaning of this word in this context. Joël Lamotte
2016-09-20 11:12 GMT+02:00 Klaim - Joël Lamotte <mjklaim@gmail.com>:
On 16 September 2016 at 15:23, Andrzej Krzemienski <akrzemi1@gmail.com> wrote:
Hi Everyone, I would like to ask your opinion, suggestions, and perhaps an informal review of the 'markable' library. An alternative to boost::optional when performance really matters. It is the second incarnation of the 'compact_optional' library that I have presented last year.
The main differences from the previous version: * name change: it is called 'markable' so that it is not confused with 'optional'. It uses "mark values" and mark policies" * it has the ability to mark an empty state even for the types with strong invariants, by marking a bit pattern rather than any proper value of T (an idea by Matt Calabrese)
An example use case:
typedef markable<mark_int<int, -1>> opt_int;
opt_int oi; opt_int o2 (2); assert (!oi.has_value());assert (o2.has_value());assert (o2.value() == 2); static_assert (sizeof(opt_int) == sizeof(int), "");
The github repository: https://github.com/akrzemi1/markable And documentation therein: https://github.com/akrzemi1/markable/tree/master/documentation
Any comments, suggestions or critique are most welcome.
Regards, &rzej;
I am interested in doing a quick review of this library, but I have first to ask why "markable"? I'm not sure I understand the intended meaning of this word in this context.
Joël Lamotte
Hi Joël, Thank you for expressing your interest. You bring up a good point. The docs lack rationale for the naming. I will try to update them soon.
The reason is somewhat psychological or "social"; and at the bottom boils down to the fact that the compromise around std::optional in The Standards Committee is very fragile, and decisions around `markable` may affect it. Clearly, there is some overlap between `optional` (boost::optional, std::optional) and `markable`. I was explicitly requested to remove any 'optional' from this library's name, in order not to make any suggestion that I am competing with `optional` or that I am offering a superior alternative -- I am not. You can look at my library from two perspectives: 1. I am giving you a more efficient alternative to optional<int>, at the cost of compromising the interface. 2. It is just an `int` with some magical value, say -1, but I am making it more explicit that this `-1` marks a special case. In this second sense, "mark", "markable", "marked" quite naturally reflect the fact that I am marking one value as special, which is the core idea behind this library. One could consider other alternatives like "magic_val", but they are not much better, and do not transform into adjectives and verbs that easily ("magic_valued"?). Does that sound convincing? Regards, &rzej;
On 20 Sep 2016 12:04 pm, "Andrzej Krzemienski" <akrzemi1@gmail.com> wrote:
[...].
One could consider other alternatives like "magic_val", but they are not much better, and do not transform into adjectives and verbs that easily ("magic_valued"?).
Does that sound convincing?
Maybe. More seriously, yes please submit it to boost, I have seen reinvented (a few by myself) this too many times. A few comments: * Consider, maybe, a different name (OK, OK I'll stop). * The first template parameter should be the stored T itself, pass the policy as a separate optional parameter. * Pick a sensible default policy. I like nan for floats, otherwise value initialisation is a good default. For integers is easy to have a policy specifying the value inline. * to avoid the bag of functions issue with policies, consider using ADL for customisation and use the stateless policy just to drive the lookup. * The policy itself can be used for tagging, no need for an extra parameter. * Markable should be trivially copyable, assignable and destructible when the underlying T is, at least when not using the pod storage policy. This might be already the case. * Add flatMap? That's all for now, -- gpd
On 21 Sep 2016 12:32 am, "Giovanni Piero Deretta" <gpderetta@gmail.com> wrote:
On 20 Sep 2016 12:04 pm, "Andrzej Krzemienski" <akrzemi1@gmail.com> wrote:
[...].
One could consider other alternatives like "magic_val", but they are not much better, and do not transform into adjectives and verbs that easily ("magic_valued"?).
Does that sound convincing?
Maybe.
More seriously, yes please submit it to boost, I have seen reinvented (a
few by myself) this too many times. A few comments:
* Consider, maybe, a different name (OK, OK I'll stop). * The first template parameter should be the stored T itself, pass the
* Pick a sensible default policy. I like nan for floats, otherwise value initialisation is a good default. For integers is easy to have a policy specifying the value inline. * to avoid the bag of functions issue with policies, consider using ADL for customisation and use the stateless policy just to drive the lookup. * The policy itself can be used for tagging, no need for an extra
* Markable should be trivially copyable, assignable and destructible when
policy as a separate optional parameter. parameter. the underlying T is, at least when not using the pod storage policy. This might be already the case.
* Add flatMap?
That's all for now,
Oh, I forgot: from the docs: "It is impossible to alter the "contained value" through the access to function value[...] With the assignment being prevented, we could get the following surprising behaviour: marked<mp_int<int, -1>> oi {1}; oi.value() = (int)some_value; // < suppose it was allowed assert (oi.has_value()) // < might fail! " I don't find it more surprising than this: marked<mp_int<int, -1>> oi {1}; oi = marked<mp_int<int, -1>>(some_value); assert (oi.has_value()) // < might fail! This should be allowed as long as the storage type is really T ( in fact I suggest in this case making the underlying field public and allowing aggregate initialisation).
2016-09-21 1:32 GMT+02:00 Giovanni Piero Deretta <gpderetta@gmail.com>:
On 20 Sep 2016 12:04 pm, "Andrzej Krzemienski" <akrzemi1@gmail.com> wrote:
* Add flatMap?
Thanks for the feedback. I am just reading through it. Just one immediate question. What do you mean by "Add flatMap" here? What would it do? Regards, &rzej;
On 21 Sep 2016 7:21 am, "Andrzej Krzemienski" <akrzemi1@gmail.com> wrote:
2016-09-21 1:32 GMT+02:00 Giovanni Piero Deretta <gpderetta@gmail.com>:
On 20 Sep 2016 12:04 pm, "Andrzej Krzemienski" <akrzemi1@gmail.com>
wrote:
* Add flatMap?
Thanks for the feedback. I am just reading through it. Just one immediate question. What do you mean by "Add flatMap" here? What would it do?
Apply a function f to the contained element of the markable<T> (if non empty). If f is from T->U returns markable<U>, if it is T->markable<U> simply returns that. Exactly like std::future::then. In fact it should probably be called 'then' instead of flatMap. Optional should also have it of course. -- gpd
On 16 Sep 2016 at 15:23, Andrzej Krzemienski wrote:
An example use case:
typedef markable<mark_int<int, -1>> opt_int;
opt_int oi; opt_int o2 (2); assert (!oi.has_value());assert (o2.has_value());assert (o2.value() == 2); static_assert (sizeof(opt_int) == sizeof(int), "");
I'm not sure about the value add of this simplified optional implementation. What compelling use cases does it have over optional? In my own boost::outcome::option<T>, it's just one of many specialisations of basic_monad<T, EC, E> and has all the advantages of being a basic_monad, including implicit convertibility to more expressive basic_monad's, monadic operators etc. It also has the same ridiculous optimisability that basic_monad has and typically reduces down to no runtime overhead (which from inspection so does your markable). Yours does have likely very low impact on compile times though. Mine does stress the optimiser in release builds a lot. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
2016-09-21 18:35 GMT+02:00 Niall Douglas <s_sourceforge@nedprod.com>:
On 16 Sep 2016 at 15:23, Andrzej Krzemienski wrote:
An example use case:
typedef markable<mark_int<int, -1>> opt_int;
opt_int oi; opt_int o2 (2); assert (!oi.has_value());assert (o2.has_value());assert (o2.value() == 2); static_assert (sizeof(opt_int) == sizeof(int), "");
I'm not sure about the value add of this simplified optional implementation. What compelling use cases does it have over optional?
Compared to optional: 1. no spacial overhead for storing "initialized" flag: sizeof(markable<mark_bool>) == sizeof(bool); sizeof(markable<mark_int<int, -1>>) == sizeof(int); 2. More type safe: no implicit conversions; also built-in opaque typedef-like tool for generating similar, but not interchangable types 3. The ability to "remove certain values of T. Suppose you want to return a std::string, but you want to signal to the callers (by the means of type-system) that they should treat an empty string in a non-uniform way. If you do it with optional<string>, you get this useless state: bool useless_state(optional<string> os) { return os != boost::none && os->empty(); } With markable, when you treat empty string as "empty" markable, you are guaranteed that the contained string is never empty: void guarantee(markable<mark_stl_empty<string>> os) { if (os.has_value()) assert (!os.value().empty()); } In my own boost::outcome::option<T>, it's just one of many
specialisations of basic_monad<T, EC, E> and has all the advantages of being a basic_monad, including implicit convertibility to more expressive basic_monad's, monadic operators etc. It also has the same ridiculous optimisability that basic_monad has and typically reduces down to no runtime overhead (which from inspection so does your markable).
Yours does have likely very low impact on compile times though. Mine does stress the optimiser in release builds a lot.
Compared to boost::outcome::option? I do not know boost::outcome::option that well? Are its characteristics documented anywhere? Thank you for response, &rzej
participants (4)
-
Andrzej Krzemienski
-
Giovanni Piero Deretta
-
Klaim - Joël Lamotte
-
Niall Douglas