I feel like Haskell is easier to use than it is to explain, and in my experience a lot of these kind of tutorial / explanations actually make things seem harder and more complicated than just working with the concepts and observing what they do. (This one included.)
I'm not familiar with Haskell and am really, really struggling to follow the article.
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 one mistake is everyone trying to explain a Haskell concept to the general population makes is using Haskell. If someone already knows Haskell there is a good chance they know there concepts. Don't use Haskell as the language, use js to explain it.
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 am not yet sure whether it's a third mistake to think that it's particularly relevant to understand monads (and friends) outside of a language with (a) the higher-kinded types necessary to actually use them, and (b) a type system that is inconsistent in the face of effects without monads.
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.
> 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 was lucky seeing this before hitting submit button. Phew that was close.
The distinction is that in general “opening a box and extracting the value” makes no sense, as it's not a thing that can be done in general. If your box is a Maybe, there might not be a value to extract. If it's a list, there might be zero or multiple values. It only ever makes sense to map over the contents of the box, replacing the values with their image under the map.
To try to answer your first question, coming form someone who is also not an expert in Haskell or monads.
"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.
Every “hard” concepts I’ve seen in Haskell is immediately clear to me if explained in almost any other language. The hard part is Haskell, not the concept.
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.
Haskell has a type system that lets these things be directly useful in ways they cannot be in many other languages.
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.
Why are there so few practical, example and code driven tutorials? I've never run across a succinct "build Twitter with Haskell" in the wild.
This talk seems like exactly what you are looking for for:
Gabriel Gonzalez - “A bare-bones Twitter clone implemented with Haskell + Nix” @ ZuriHac 2020 https://www.youtube.com/live/Q3qjTVcU9cg
Yes, I really need a real word Haskell project simple enough to understand all the math concept. Like, I don't know when to implement the Monad type-class to my domain data types. For example, taking the twitter example, if I have Tweet data type:
- 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.
> Yes, I really need a real word Haskell project simple enough to understand all the math concept
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`
> should I implement the Monad, Applicative or Functor type class?
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.
> should I implement the Monad, Applicative or Functor type class?
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.
Highly recommend Richard Eisenbergs video series on building a Wordle solver https://youtube.com/playlist?list=PLyzwHTVJlRc9Fcinmxe97pHl_...