ptal / expected

What did you expect?
113 stars 18 forks source link

[Monads] Should the default traits forward? #44

Closed viboes closed 10 years ago

viboes commented 10 years ago

Currently the value_traits forward all the operation fro the default category, that is the type itself. As a concept contains semantic constraints in addition of the syntactic ones, it would be better if the model designer states explicitly that the forward is correct way to map the type.

So I purpose to add a forward category and replace the default with this category.

  template <class T>
  struct value_traits : std::false_type {} ; // SFINAE friendly

  template <class T>
  struct value_traits<category::forward> : std::true_type  // SFINAE friendly
  {
    template <class M>
    static constexpr bool has_value(M&& m)
    { return m.has_value();}

    template <class M>
    static constexpr auto deref(M&& m) -> decltype(m.deref())
    { return m.deref();};

    template <class M>
    static constexpr auto get_value(M&& m) -> decltype(m.value())
    { return m.value(); };
  };
viboes commented 10 years ago

https://github.com/ptal/Boost.Expected/commit/731e05603e132269026869984d82b670b1cc2aca

viboes commented 10 years ago

https://github.com/ptal/Boost.Expected/commit/85f2a73b384dd3e64d8bdfcb41dad0f1c8b9c7a9

ptal commented 10 years ago

What about "default"? I'm afraid we confuse this category with the "forward_iterator" category. Also the notion of forwarding here is a detail of implementation, the notion of "default" (or another name) explicitly state that it's the default behaviour. What do you think?

viboes commented 10 years ago

I'm open to other names, but "default" is not good for two reasons:

Sorry for the title, it should be, Should the default trait be defined?

ptal commented 10 years ago

Would "identity" be right? I think it's a good thing to define this default trait.

viboes commented 10 years ago

What we are implementing is forwarding, as we transform a non member function on one member function that has the same name and we forward the other arguments.

Why do you have problems with forward?

ptal commented 10 years ago

Well I don't really have a problem with it, it's just that it sounds really implementation-oriented and I was wondering if there was something more general underneath. But I'm ok with it.

viboes commented 10 years ago

Defaulting to forward would mean that any class supporting these functions syntactically would be be a model of the concept. A concept is much more than syntactic constraints and so an explicit mapping is desirable. The single thing the developer must do is to state it explicitly,e.g.

  template <class T, class E>
  struct errored_category<expected<E, T> > : mpl::identity<category::forward> {};

We could comeback to the default to forward if the model provide these member and or types as members, but the definition of the default is much complex. I implemented it just before deciding to add an explicit category. The code e.g. would be

template <class T, class = void>
   struct value_traits_h  : std::false_type {};
template <class T>
   struct value_traits_h<T,
    void_t<
      typename T::value_type,
      decltype(std::declval<T>().has_value()),
      decltype(std::declval<T>().deref()),
      decltype(std::declval<T>().value())
    >
  > : std::true_type {
// forward
};

This is needed if we want the valued_traits to be used SFINAE friendly. There are some proposals related to this SFINAE friendly, as result_of, common_type, iterator_traits. I think that these traits should also be SFINAE friendly.

Now we can use

  template <class PV, class U, class = std::enable_if<valued_traits_t< PV >::value> >
  constexpr value_type<PV> value_or(PV const& e, U&& v)  {
    return has_value(e)
      ? deref(e)
      : static_cast<value_type<PV>>(std::forward<U>(v));
  }
viboes commented 10 years ago

I have found a use for a category::default_. Some concepts define a default implementation for some operations. This default implementations could be added to a xxxtraitscategory::default. E.g. in Haskell there is a mdo operator(>>) that is defined as

  template <class M, class T, class U>
  apply<M,U> mdo(apply<M,T>&& m1, apply<M,U>&& m2)
  {
    return mbind(m1, [&](T& ) { return m2; });
  }

Any xxx_traits specialization could inherit from xxx_traits<category::default_>. What do you think?