boostorg / hana

Your standard library for metaprogramming
http://boostorg.github.io/hana
Boost Software License 1.0
1.7k stars 216 forks source link

Rename Monad::bind #9

Closed ldionne closed 9 years ago

ldionne commented 10 years ago

The name is misleading because of the existence of std::bind. If anyone can come up with a better name, please suggest it.

viboes commented 10 years ago

Yes, there are at least 3 major differences 1 the order of parameters 2 monad::bind calls the continuation while std::bind create a functor.

std::experimental::future<T> has `then(), but the continuation takes thefuture`` as parameter and it has an asynchronous connotation.

I suggested the addition of next with a continuation taking Tbut it also has an asynchronous connotation.

apply seems better, but the order of parameters should be changed. This name is also appropriated as a replacement for fmap.

What do you think of overloading apply to cover fmapand bind?

apply :: (a -> b) -> f a -> f b
apply :: (a -> m b) -> m a -> m b
ldionne commented 10 years ago

We totally agree that bind is not the right name. However, apply is already used to denote function application (see boost/hana/functional/apply.hpp).

Also, I do not think fmap should be renamed to apply; I think the intent is much more clearly expressed by saying fmap (mapping a function), and that name (or close variations thereof) is already used in other programming languages like Python.

That being said, I am not closed to the idea of renaming bind to apply, and then finding a new name for apply. Or even better: if we could come up with a data type for functions, and if that data type was the instance of a type class which provided a method equivalent to apply, then I would happily use that method instead of apply and we could rename bind to apply. That option might even make us able to generalize (and hopefully rename) some other functions from the Functional module like demux.

As for the idea of overloading the name apply, I am reluctant to do so at first sight. I prefer to keep things more explicit (especially in the case of such similar-looking functions) to avoid confusion. Keeping things explicit forces one to stay honest and keep track of what is inside a Functor and what isn't in a more rigorous way.

wolftype commented 10 years ago

extend ? pipe (cause of | ) fjoin ( that's ugly)

Nice work on this Louis!

ldionne commented 10 years ago

extend is already commonly used for Comonads. Since I think there's a nice generalization of the current Logical type class using Comonads, I might need that name later on. Otherwise, it might make sense to use extend; I'll consider that.

pipe might make sense too. I was reluctant at first sight, but it really might make sense.

fjoin is too ugly.

Thanks for the suggestions!

pfultz2 commented 10 years ago

Actually, I implemented the state monad in C++, just as an exercise(see here). I used for_ for bind and yield for return so there is no double return at the end. I also had a do_ for the then operator. It seems those terms would be more familiar to C++ programmers. I chose for_ since it introduces a new scope and variables, it seemed fitting. Just my two cents.

ldionne commented 10 years ago

That's a very interesting gist. I'll have a deeper look at it when I get the time (exams right now), but I think this might be a nice direction to take.

ldionne commented 9 years ago

Two new suggestions:

chain(monadic_result, monadic_function)
monadic_apply(monadic_function, monadic_result)
ldionne commented 9 years ago

Note that monadic_apply would be consistent with the recently added monadic_fold. I still don't like it as much as chain (or pipe) though.

pfultz2 commented 9 years ago

Is it possible to combine fold with an overload for monadic_fold? Or perhaps there is a way to annotate a function to signify that it returns a monadic type?

ldionne commented 9 years ago

You mean provide only fold and use a monadic variant whenever the function returns a monadic result? This is not possible right now, because we don't know the generalized type returned by functions. It would be possible to annotate functions so we have that knowledge, e.g.:

auto f = monadic_function<Monad>([](auto x) { ... });
fold(xs, state, f); // uses a monadic fold

Is this what you mean?

pfultz2 commented 9 years ago

Is this what you mean?

Yes. Also, I wonder if Monad could be deduced at a later stage. So essentially you could write:

auto f = monadic_function([](auto x) { ... });
fold(xs, state, f); // uses a monadic fold
ldionne commented 9 years ago

I assume you mean that Monad would be deduced when we call monadic_function(f) on an argument. This way, it could deduce it from the return type of the wrapped function f. Is this what you mean?

If so, this is a brilliant idea, but unfortunately it won't work. monadic_fold must know the Monad even when the function is never called, for example in the case of an empty sequence. This is required so it can lift the state into the proper Monad.

It would be possible to wrap the state in a "yet-to-be-determined" Monad, by using e.g. lift(state) instead of lift<M>(state). I'm not exactly sure what that would buy us and what it might complicate, but I would say it is worth thinking about.

I personnally like the fact that monadic_fold must be written explicitly, since they really are two different things. If we can find a nice way (e.g. a proper generalization of folding and monadic folding) to make fold and monadic_fold coincide then I would be interested, but otherwise I don't see this as a defect.

pfultz2 commented 9 years ago

but unfortunately it won't work. monadic_fold must know the Monad even when the function is never called

Oh yea thats right.

I personnally like the fact that monadic_fold must be written explicitly, since they really are two different things

Yes it does make sense to have two seperate functions, as well. I was just trying to think of a possible different approach.

viboes commented 9 years ago

An alternative to renaming to avoid syntactic or semantic conflicts with other names is, in C++, to use namespaces to name type classes.

auto x = monad::bind(m, f);
auto y = functor::map(f, m);

As Haskell as no namespaces, it needs to use prefix f, m such as in fmap, mzero, mplus.

I'm not saying that bind and map are the best names, just that adding the type class give us more freedom.

ldionne commented 9 years ago

While it technically gives us more freedom, in practice we then have to type monad::bind all around or using namespace boost::hana::monad. I find this unattractive. Also, when you push the concept, you end up with foldable::fold.left, sequence::remove_at, searchable::find_if and so on, which is cumbersome. We don't have major naming conflicts in the library right now as I've managed to name things in such a way that everything fits. Of course, the names are not perfect (as reflected by this thread), but I don't have fmap and mzero and so on either. I'd rather not use namespaces.

ldionne commented 9 years ago

I'm about to close this. I decided to rename bind to chain. I also want to thank @pfultz2 for suggesting the monadic_function thing; this made me realize that I could remove template parameters in some cases by deferring the computation, which I just did with mcompose (now called monadic_compose by the way).

ldionne commented 9 years ago

fixed by fb518d859afd9fd4fd0e4a4b6c1fc826fd7ac1ae