5 ms·
His 'plain english' descriptions are really just the definition in Haskell. Haskell defines one or two more methods for efficiency, but if you ignore the entrie
by MichaelBurge 11y ago
His 'plain english' descriptions are really just the definition in Haskell. Haskell defines one or two more methods for efficiency, but if you ignore the entries with default implementations you get:
1. "Functors are containers you can call map on. That's it. Seriously."
class Functor f where
fmap :: (a -> b) -> f a -> f b
Historically, fmap in Haskell used to be called map. They gave it a different name so that error messages would appear less abstract when working with lists.
fmap (+2) [5] = [7]
2. "you may want to have a function in the container and apply it to value(s) in another container of the same kind. "
class Functor f => Applicative f where
-- | Lift a value.
pure :: a -> f a
-- | Sequential application.
(<*>) :: f (a -> b) -> f a -> f b
So at least in Haskell, he describes the sequential application operator but leaves out the ability to lift a pure value to a container value.
pure 5 = [5]
[(+2)] <*> [5] = [7]
3. "Monads are containers you can call flatMap on. Again, that's it."
class Applicative m => Monad m where
-- | Sequentially compose two actions, passing any value produced
-- by the first as an argument to the second.
(>>=) :: forall a b. m a -> (a -> m b) -> m b
And here's how to use it like a flatmap:
[1,2] >>= (\x -> [x + 5, x + 10])
[6,11,7,12]
That's [1 + 5, 1 + 10, 2 + 5, 2 + 10] if you didn't catch that.
Usually these also come with some laws to help you reason about the typeclass, but looking purely at the language level it can help to remember that a Functor, Applicative, or Monad are nothing more than a type together with some functions with a certain signature.
I find it's usually more useful for a beginner to just start writing code without all the abstractions. Once they have a pile of code and start to want to clean it up a bit, it's not too hard to start stacking some monad transformers.
- Guvante 11y agoI think the dilemma with Haskell typeclasses isn't the "how", that is fairly straightforward especially for lists and maybe. Instead it is the "why". Groking lens was probably the first time I truly understood why type classes have laws. Unfortunately no one has found an easy path to figuring that out, it takes a long time to truly understand the mechanics of the type classes and a bit longer still to understand why they are useful.
- catnaroek 11y ago> why type classes have laws. No idea about other communities, but C++ programmers have understood this for ages. Stepanov put it quite nicely: “Algorithms are defined on algebraic structures.” And algebraic structures are defined by their equational laws.
- Sirenos 11y agoI always found it a bit amusing that learning Haskell actually helped me get a deeper understanding of C++ templates and metaprogramming.
- Buttons840 11y agoWhat's a monad transformer? I kind of know what a monad is, but what is a transformer? Functional programming sure does use some scary words. I guess OOP has it's fair share as well, with inheritance, subclasses, superclasses (which are much more super than your lowly subclass apparently), etc.
- MichaelBurge 11y agoIf a monad is like giving some additional context to a value: Maybe a: "Either it's a value or nothing" [a]: "The value was obtained from a list of possible values" IO a: "The value was obtained from a database somewhere" Except ErrorType a: "The value is either an error message or a real value" , then a monad transformer takes an existing monad and augments it with even more context: ExceptT ErrorType IO a: "this value was obtained from a database somewhere, and also maybe there was an error message" StateT ConnectionPool IO a: "this web server response value was calculated using a connection obtained from the server's stateful connection pool, and a database connection" Most monads like State are actually defined as the monad transformer applied to the Identity monad, which has no special context at all: State stateType returnType = StateT stateType Identity returnType Additionally, IO is very common to bury at the bottom of one of these stacks, so there is 'liftIO' to run an IO action in a monad transformer stack. So your web server can just do: liftIO $ putStrLn "Hello World" if you need a quick log message. You need liftIO, because 'putStrLn "Hello World"' is of type IO (), and the web server needs type 'StateT ConnectionPool IO ()'. Note: IO does more than just databases, of course.
- mezuzza 11y agoI like to think of it as a way to combine monads. Two classic examples might be the State monad and the Maybe monad. So let's say you want to create a function that operates on some state and might return a value. These are clearly both monads that you've heard about in Haskell, but how do you use them together? The answer is to use a monad transformer. StateT is the state monad transformer that takes any other monad and adds state to it. `StateT s m a' is a monad that adds a state of type s to a monad m. So `StateT Int Maybe a' is thing that when given an int which is its state, might return a value. This particular combination was a little esoteric, so I'm not sure what a great example of this would be. However, you could also think of a situation where you want to have some sort of read-only state (such as flags from the command line) and the ability to write to a log. In Haskell these are provided by the Reader and Writer monads. But again, you have two separate monads here and in order to actually make use of them, you'll need to combine them somehow. Enter a monad transformer. You can either transform a Writer monad with the ReaderT monad transform or transform a Reader monad with the WriterT transform. ReaderT r (Writer w) a OR WriterT w (Reader r) a Both satisfy this use case. I think those two constructions should be equivalent and conceptually equal. Someone should correct me if I'm wrong and there is some actual reason that those are different. To my understanding, the difference is purely due to limitations of the language as opposed to the mathematical constructs.
- catnaroek 11y ago> Usually these also come with some laws to help you reason about the typeclass, but looking purely at the language level it can help to remember that a Functor, Applicative, or Monad are nothing more than a type together with some functions with a certain signature. Respecting type class laws isn't optional: The laws are an intrinsic part of the abstraction's definition, so if your instances don't respect them, your code is simply wrong.
- MichaelBurge 11y agoblaze-html is an example of something implementing the Monad typeclass just to get desugaring. Somebody wrote Lucid for exactly the reason you describe(you can't use things like forM_ with blaze because it blows up). In any case, you might argue it isn't a mathematical monad, but it can still be a Haskell monad as long as it implements the typeclass. I agree it makes the most sense if it implements the laws: Otherwise, it's like calling something a Stack<String> when it really deletes your home directory whenever you peek an element. Technically anything that implements the Stack<> interface is a Stack in the language, but it's not an abstract Stack.
- asQuirreL 11y agoI would argue that the whole point of having a type class is the laws. It allows the programmer to assume certain properties are true that the type checker cannot verify. Just because the language lets you write an instance that does not conform to a class' laws, does not mean it is the right thing to do, it is precisely because the language cannot check them that those laws exist.
- catnaroek 11y ago> In any case, you might argue it isn't a mathematical monad, but it can still be a Haskell monad as long as it implements the typeclass. This attitude is unacceptable, because it undermines the meaning of abstractions, on which much of the Haskell ecosystem critically depends. It is also a potential turnoff for those who want to learn Haskell but find the learning curve too steep: "What use is investing so much time and effort learning abstractions that people will routinely break anyway?"
- stevetrewick 11y agoNot everyone reads Haskell. In fact I'd be willing to bet that only a vanishingly small number of working programmers can fluently read Haskell notation at this point in time. This article in particular is about Swift. One of the barriers to understanding these concepts is that they are often written about as though throwing in a bind operator on a list monad in Haskell makes the author's point blindingly obvious rather than opaque.