pub fn read<P>(path: P) -> io::Result<Vec<u8>>
where
P: AsRef<Path>,
{
fn inner(path: &Path) -> io::Result<Vec<u8>> {
let mut bytes = Vec::new();
let mut file = File::open(path)?;
file.read_to_end(&mut bytes)?;
Ok(bytes)
}
inner(path.as_ref())
}
Plus, your example does not have the same semantics as the Rust code. You omitted generics entirely, so it would be ambiguous if you want monomorphization or dynamic dispatch. Your `bytes` and `file` variables aren't declared mutable. The `try` operator is suddenly a statement, which precludes things like `foo()?.bar()?.baz()?` (somewhat normal with `Option`/`Error`). And you weirdly turned a perfectly clear `&mut` into a cryptic `&!`.
A monad is a specific variant of a higher kinded type whose primary property lies in the fact that its value can be "evolved" via a specific function application (usually known as the "bind" operation). This sounds confusing but it's really a simple concept: if you take a higher kinded type, you can think about the values within it as the data and the type itself as metadata or a context of some sort. For example, an int is a piece of data, but a `Maybe int` is the same data with the added context of it being possibly absent.
A monad is not the only higher kinded type. Probably the most familiar HKT to working programmers (even if they don't know it) is the functor, which is a type that can be mapped over (in the sense of map-filter-reduce). If you have a `Foo<A>`, you can apply a function to turn it into a `Foo<B>`. However, a limitation of the functor is that you can only affect the data at the individual item level, not the context as a whole, so if you put a list of five values in, you'll get a list of five values back.
If we want to affect the context as well, we instead need a type like the monad, where we can "bind" a function that takes the inner value and returns a whole new wrapper based on it.
Why is this useful? Well, it can express a lot of different things very neatly - for example, fallible computation. Let's say you have two functions that both return a Maybe monad. In a language with nulls, you'd likely have to do something like `a = foo(); if (a is null) return; b = bar(); if (b is null) return`, which is tedious and error prone. Monads lend themselves to composing such chains extremely well, so you can simply do `foo().and_then(_ => bar())` and in the end have a value that combines the result (success or failure) of both of those functions.
Null values = chains of null checks = monads! Futures = chains of callbacks = monads! Mutable state = chains of writes = monads! Sequential execution = chains of statements = monads! And so on and so forth. You can get pretty crazy with it, not that I would recommend it.
Monads seem complicated and scary because most mainstream programming languages don't have the necessary abstractions to talk about higher kinded types as its own thing, but in reality most programmers are using monads daily without even realizing.