Monads are the wrong abstraction IMHO. Functors are all you need to have "monadic bind" syntax, what monads give you is two more natural transformations (per monad) that let you collapse any number of levels of nesting of your monad down to one level. eg. The `pure`

and `flatten`

operations on `Option`

allow you to convert:

```
Option<Option<...<Option<T>>...>> --> Option<T>
^~~~~~~~ n times ~~~~~~~^
```

Which is what allows you to write `try`

blocks (for `Option`

) with `n`

uses of the `?`

operator for any `n`

.

These aren't the only important natural transformations though. For instance, using `?`

on a `Result`

will also convert the error type using `Into`

. That is, it uses a generic natural transformation:

```
Result<Result<T, E0>, E1> --> Result<T, E>
where
E0: Into<E>,
E1: Into<E>,
```

This is more general than the `flatten`

operation that you get from `Result<_, E>`

being a monad.

Another example: If you want to be able to use `?`

inside `async`

blocks then you also need to be able to transform `impl Try<Ok = impl Future<Output = T>>`

into `impl Future<Output = impl Try<Ok = T>>`

, but you don't get this from monads.

So I think if you want to support "monadic" syntax the way to do it is to ditch monads completely and work directly at the level of functors and natural transformations.

How I imagine this working is: for any functor (any trait with a `map`

method) you can give it its own block/bind psuedo-keywords (eg. `async`

/`await`

in the case of `Future`

). You also have to provide an "into" trait which is implemented on types that can be converted into your functor. eg. For `Future`

this would be `IntoFuture<T>`

which is implemented for types that can be converted into an `impl Future<Output = T>`

. The implementations of this trait provide the natural transformations, so you need to at least have `T: IntoFuture<T>`

and `F: IntoFuture<T> for F: Future<Output = G>, G: Future<Output = T>`

if you want the monadic `pure`

and `flatten`

operations. But you can also provide other implementations such as the one that swaps the order of `Try`

and `Future`

above.

When you use your new block syntax all the binds get translated into invocations of `map`

and the entire body of the block is passed to a call to your "into" trait. For example, if we had the following block:

```
async {
let b = a.await;
let c = b?;
let x: T = foo(c);
x
}
```

This gets translated into (something equivalent to):

```
IntoFuture::<T>::into({
Future::map(a, |b| {
Try::map(b, |c| {
let x: T = foo(c);
x
})
})
})
```

Note that it can't literally be translated like this due to borrowing, but if you wanted to implement this with macros you can probably expand it to something equivalent using generators and `unsafe`

hackery.