In the case of the functor, the author doesn't explain in technical, specific enough terms the difference between "open the box, extract the value out of it, apply the function, and put the result back in a box" and "apply a function to a box directly; no need to perform all the steps ourselves." I have no idea what 'apply a function to a box' even means.
> That’s the essence of functors: an abstraction representing something to which we can apply a function to the value(s) inside
The error in this sentence garbles its meaning beyond recovery. "We can apply a function" governs two prepositional phrases that are semantically and syntactically identical: "to which;" "to the value(s) inside." There's no way to resolve the meaning of one without rendering the other incoherent.
The number two mistake people make is being aware of the number one mistake so they go write yet another Monad tutorial in Javascript (or Java or whatever...). Which is why there are so many damn Monad tutorials, all saying pretty much the same thing.
I waver between a belief that developers with curiosity about computer science topics will, over time, be quantitatively better developers, and the notion that these are niche topics with limited relevancy.
After all, it's very clear that the Java standard library design committee understands what monads are and where they're useful, since the library is littered with the things, but there's vast numbers of developers out there making effective use of futures, collections, optionals, and streams, building their own intuitions about what "flatMap" means you can get away with, all without reading any monad tutorials.
I was lucky seeing this before hitting submit button. Phew that was close.
"apply a function to a box directly; no need to perform all the steps ourselves."
The box doesn't change, and it also doesn't matter what's inside of it. You are attaching the function to the box, who later knows how to apply it to itself. If you were to open the box, you would need to know how to handle all the possible contents. It's key here that you are only handling a box and nothing else.
Usually I’m left wondering why whatever-it-is even has a name, it’s so simple and obvious and also not that special or useful seeming, it’d never have occurred to me to name it. I guess the people giving them names are coming at them from a very different perspective.
Exception: type classes. Those are nice and do need a name.
You can't, in Java, declare anything like "class Foo<F<T> extends Functor<T>>", or use a similar generic annotation on a method: you can't have an unapplied type-level function as an argument to another type-level function.
These things get a name in Haskell because they can be directly used as useful abstractions in their own right. And perhaps because Haskell remains very close to PL research.
Gabriel Gonzalez - “A bare-bones Twitter clone implemented with Haskell + Nix” @ ZuriHac 2020 https://www.youtube.com/live/Q3qjTVcU9cg
- should I implement the Monad, Applicative or Functor type class?
- How would that help in the big picture?
- What if I don't do it?
All these funny example of boxes, burritos or context doesn't not help me solve problems.
Take for example Monoid, I understand (partially maybe) that it useful for fold (or reduce) a list to a single value.
There actually is a book with precisely that title, which provides what you're asking for: https://book.realworldhaskell.org/
> Like, I don't know when to implement the Monad type-class to my domain data types
A concrete type (such as your Tweet type) can't be a Monad. Monad is implemented on generic types (think: `MyType a`, where `a` can be filled in with a concrete type to produce e.g. `MyType Int` or `MyType String`).
Most monads are data structures like list `[a]` or structures which provide context to computations like `State s a` or `Reader r a`
You rarely have to implement these type classes. But you need to understand how they work since many libraries use them. If you do IO, error handling, concurrency, use containers, option parsing and so on, you'll have to use these type classes.
For your own types, nobody forces you to implement them. If it turns you can make your type an instance of some type class, you may be able to reuse existing code rather than reimplementing it. And it will make the program more readable too.
I struggled with this when I first learned Haskell. The answer is "yes, if you can". If you have a type, and you can think of a sane way to implement `pure`, `fmap`, and `bind` that doesn't break the algebraic laws, then there's really no drawback. Same for any typeclass. It gives users access to utility functions that you might not really have to document (because they follow a standard interface) and you might not even have to maintain (when you can just use `deriving`).
Doing so will let you/users write cleaner code by allowing use of familiar tools like `do` notation, or functions from libraries that say they'll work for any Monad. It saves you from coming up with new names for those functions, and saves users from having to learn them; if I see something's a Monad, I know I can just use `do` notation; if I see something's a Monoid, I know I can get an empty one with `mempty` and use `fold` with it. As long as it's not a really strange Monad, and it doesn't break any laws, it probably just works the way it looks like it does.
If you can define `bind` et. al., but it breaks the laws, it means the abstraction is leaky - things might not work as expected, or they might work subtly differently when someone refactors the code. Probably don't do that.
If you don't implement a typeclass that you could have, it just means you might have written some code where you could've used something out of the box. Same as going through old code and realizing "this giant for-loop could've just been a few function calls if I used underscore/functools or generators".
That said, it's not too common to stumble on a whole new Monad. The Tweet type probably isn't a Monad - what does it mean for a Tweet to be parameterized on another type like `Int`, as in `Tweet<Int>`? What would it mean to `flatMap`(`bind`) a function like `Int -> Tweet<String>` on it? A Tweet is probably just a Tweet. On the other hand, it's a little easier to imagine what a `JSON<Int>` might be, and what applying a function like `Int -> JSON<String>` to it might reasonably do. Or what applying an `Int -> Graph<String>` to a `Graph<Int>` might do.
Most Monads in practice are combinations of well known ones. Usually you'll be writing some procedural code in IO, or working with a parser, and realize "I'm writing a lot of code checking for errors", "I'm tired of explicitly passing this same argument", or "I need some temporary mutable storage", or some other Effect - so you wrap up the Monad you're using with a Monad Transformer like `ExceptT`, `ReaderT`, or `StateT` in a `newtype`, derive a bunch of typeclasses, and then just delete a bunch of messy code.