What I Wish I Knew When Learning Haskell
dev.stephendiehl.com
dev.stephendiehl.com
1. ACID (acid-compliant persistance on Haskell data types, with indexes)
http://dev.stephendiehl.com/hask/#(27)
2. Pipes (program on streams of data)
http://dev.stephendiehl.com/hask/#(30)
3. Scotty (micro web-framework, a la Sinatra or Flask, on top of the super fast Warp server) http://dev.stephendiehl.com/hask/#(32)
Haskell is both strongly and statically typed. That type boundary between strings and HTML output is enforced by the compiler, and it automatically (and provably!) prevents a metric crapload of injection attacks.
For more elaboration, see http://blog.moertel.com/posts/2006-10-18-a-type-based-soluti...
[1] https://github.com/sdiehl/haskell-warp-rest/blob/master/serv...
https://github.com/sdiehl/haskell-warp-rest/blob/master/warp...
If you just present the abstract structure, you cannot understand why you even want (for example) the do notation as a language-level concept. I think this is more of a case of bad examples than anything.
Trying to represnent monads via analogy works badly too though, because monads as used in haskell are more about programming pragmatics than modelling real world objects.
The way to explain it should be :
- Show real code
- Explain why we use this structure to write our code .I think a good explanation is to ...
in Jquery you do things like chaining to build things.
$('selector').css('color','red').filter(someFilter).css('color','blue')
Chaining APIs are useful because you're combining computation('chaining'), and you don't want to have to carry around a value everywhere. You want to do the same thing in Haskell obviously.
Haskell doesn't have dot notation, so you can't do this style exactly. First idea would be to do something like
let x=jQuery document selector in let y=css x 'color' 'red' in let z=filter y someFilter in ...
This is a pain, especially considering that the 'chaining' here is mechanically obvious
let [n+1] = f [n] [whatever] in let [n+2] = g [n+1] ....
So monads, in the most pragmatic sense, allow you to represnt this form of computation. You end up with
jQuery 'selector' >>= css 'color' 'red' >>= filter someSelector >>= css 'color' blue'
You implicitly carry around your computation.
)
In any case the Maybe monad is the worst example ever, just like how a singleton group is a bad example. Sure it's the 'simplest' , but it's far from the most typical
[1] http://stackoverflow.com/questions/10496932/is-jquery-a-mona...
Obviously it is trivial to show that it isn't a monad by the fact that javascript permits any action in pure code, but by the same token, unsafePerformIO proves that Haskell has no monads. I think it's fair to operate under a reasonable subset of jQuery functionality when asking the question.
If JQuery is a monad, what are the monadic operations that make it a monad? What are its equivalents of return and bind (or any other equivalent definitions of a monad)? It's not a tricky question.
Saying that JQuery is a monad without saying what its return and bind are is equivalent to saying that whistling is Turing complete without saying how you can compute stuff with whistling. The problem here is not whether whistling is Turing complete or not, the problem is what it would even mean for whistling to be Turing complete.
This means that as of now, as long as you don't tell us what are the monadic operations of JQuery, it cannot be determined whether JQuery is a monad, because this question does not make sense yet.
So $(element) implements return as a -> [a]
And bind is mapcat, which is called by most of the jQuery methods.
Yes, that's a subset of the API, but it's the subset that distinguishes jQuery from any other collection of helper functions.
jQuery may be analogous to a monad. That doesn't make it a monad.
Can you disprove that?
(P.S. you are wronger)
Monads are more specific, and it is in that specificity that you find the requirement for monads to be "polymorphic": monads are endofunctors, and the monad laws concern the composition of those endofunctors. An endofunctor is a mapping between a category and itself. When we apply this category theory to programming, we talk about a category of types. In Haskell this is the category Hask. This is where the polymorphism requirement comes into play. The monad maps from Hask to itself. That means that it must be able to map over all types, which means polymorphism.
bind f (return a) = f a
For all a and f. Therefore, by substituting f = id, and a = return x: bind id (return (return x)) = return x
If, as in jQuery, `return (return x) = return x`, then we can simplify to: bind id (return x) = return x
Now, applying the first monad law again, we get: x = return x
So return must be the identity function. But jQuery's $ is not the identity function. Contradiction. QED.No wait, I can do better. For some values, $($(x)) is the equivalent to $(x). So, for some values $(y) = y. So for the domain of y where $(y) = y, $() is the identity function, and now it's a monad.
But yes, presto magico. I am outwitting your narrow-minded mathematics.
return :: a -> m a
in the jQuery monad?First I thought
return = jQuery
but $(".yo") :: [HTMLElement]
isnt return ".yo" :: JQuery String
I'd probably model it like this: (|>) = flip ($)
jQuery "selector" |> css "color" "red" |> filter someSelector |> css "color" "blue"
which would turn into: css "color" "blue" (filter someSelector (css "color "red" (jQuery "selector")))
class Selector a where
instance Selector Text where
instance Selector HtmlElement where
jQuery :: Selector a -> JQuery
css :: Text -> Text -> JQuery -> JQuery
filter :: Selector a -> JQuery -> JQueryreturn [Integer] will give you [[Integer]] (if you're inside the list Monad).
$($("#some-id")) will give you the same result as $("#some-id"), because $ of a $ doesn't nest (or make sense, really).
Therefore jQuery doesn't satisfy the Monad laws :).
Maybe is the simplest interesting monad, which makes it a good candidate for explaining monads to beginners.
He primarily advocates for avoiding the use of analogies, which you seem to agree with. He advocates using concrete monads in real code, which you also seem to agree with.
The only contentious point might be learning typeclasses and how monads fit into the Typeclassopedia. If you want to be able to trace how your monad-using code is evaluated this is a necessity. Like it or not. Also, attacking monads from both the concrete and the abstract at the same time seems to be a good approach, with satori being achieved once the understandings meet. A dynamic-programming approach?
You may possibly be disagreeing with the order in which he lists these components of learning in his 'Eightfold Path', but if the analogy to the Buddhist teachings is followed further, then the steps need not be taken in sequence.
The slides certainly include many monad examples besides maybe at any rate.
I think the slides are worth another look :)
f = filter (\x color x == BlueColor) . select (\x color x == RedColor)But Haskell doesn't sink in unless I use on a regular basis. The time that I've felt like I was able to see a light at the end of the tunnel (i.e., that I might be able to be useful enough in Haskell that I could keep using it regularly) was when I simply memorized the expansions for the do, <-, return sugars.
Forming a monad is a very abstract consideration. It has to do with the data type supporting a couple operations with specific properties. It doesn't say a thing about what those operations mean, so long as the required properties hold. It's up to specific monads to insert semantics into those operations.
And so yes, the IO type specifies that its monad operations mean "creating a trivial IO action that gives a specific value when executed" and "feed the result of executing an IO action into a function that returns another action." And that turns out to be a sufficient framework for composing IO actions together, once something else is providing them.
But IO isn't the only monad. There are hundreds of others already defined, and an unlimited number that can be created. What matters is that they can implement a couple functions with the required properties.
So I've said monads are abstract. What's the point then? Well, put simply, a language with the kinds of primitives Haskell has can actually put the abstraction into its libraries. This allows writing code that works at the level of the abstraction, rather than any specific implementation. The monad abstraction turns out to be rich enough to implement all kinds of conditionals, loops and recursion structures, function application patterns, etc.
In fact, work with parser combinator libraries has shown that the monad abstraction is the simplest one that allows you to express any context-sensitive grammar. If you've ever studied automata theory, bells should be ringing about now. The monad abstraction is sufficiently rich to express Turing-complete operations over the kinds of types it applies to.
And that's the real point. The monad interface allows you to specify those kinds of operations generically. Once they've been written, you can apply them to any type that provides the interface (correctly).
The fact that there's an IO type that happens to provide the interface is an accident that's only relevant historically. The benefit is much larger than that.
This kind of comes down to Rich Hickey's "simple vs easy" point. Monads are incredibly simple, but they're not easy for people who aren't used to looking for deep abstractions. One of the insights made in Haskell library design is that category theory contains several ideas that are incredibly simple and powerful as abstractions for software creation. Monad is hardly the only one - it's just the only one getting press.
In order for me to use Haskell, I need to be able to get fluent enough with it that I can be productive munging data with it. This means IO, it means a database library, regexes, and manipulating binary data.
For example, I tried to do a program that would do some HTTP stuff. The HTTP was in the IO monad, but the database connection was in a different monad. So now I'm looking into monad transformers http://book.realworldhaskell.org/read/monad-transformers.htm... (chapter 18) and my head asplodes.
I love Haskell, but it's one of the few things I've found to consistently make me feel stupid.
Now, on to the thing about Haskell that the endless proliferation of monad tutorials obscures.. You don't actually need to "understand" monads to use it. You can get by on type signatures and the most basic parts of do notation for a long time.
In fact, that's what I did. I spent months working on haskell code, understanding the transformations I was making, but not understanding the abstractions beneath them. The first time I really grasped the purpose of the monad interface was when I decided to learn how to use the parsec library. I was a bit unsure how to combine smaller parsers into more complex ones, but just sort of tried do notation - and it worked. It worked exactly how I expected it to. And that's when I realized you could see the monad interface as "The thing you need to make do notation work like you'd expect." Even if I was still fuzzy on the details.
But before that, I was making use of monad transformers. I did a poll in the #haskell IRC channel once. Almost everyone agreed monad transformers are easier to learn about than monads. There's just one simple type signature involved, and it's easy to just fit the pieces together, without a deep understanding of what they mean. They end up doing what you expect, anyway.
You really can use haskell productively without understanding the underlying abstractions, so long as you are good at following the types. They're there to help you - they do the job pretty well, if you take a bit of time to learn how to apply them.
Just for brevity. Everyone knew what I was talking about. I'm sorry. I don't like that I was imprecise and confounded monads with the IO type any more than you do.
Considering that monads in Haskell were discovered as a solution to the unweildy problem of performing I/O in a pure functional language, and that all of the early tutorials approach them from that direction, don't you think it's a teeny bit disingenuous to suddenly act like they are completely unrelated and orthogonal concepts?
Maybe I should take another crack at Haskell. But I'd just started Erlang and Riak...
The first time the term monad is mentioned, it is in the description of the agenda of the paper: "monads and input/output (Section 7)".
The second time is "May 1996. ... Monadic I/O made its first appearance, including “do” syntax (Section 7), and the I/O semantics in the Appendix was dropped."
A few more occurrences of the term and we get to section 3.2 "Haskell is pure" which says "Necessity being the mother of invention, this embarrassment ultimately led to the invention of monadic I/O, which we now regard as one of Haskell’s main contributions to the world, as we discuss in more detail in Section 7."
"Section 3.8 Haskell and Miranda ... More fundamentally, Haskell did not adopt Miranda’s abstract data types, using the module system instead (Section 5.3); added monadic I/O (Section 7.2); and incorporated many innovations to the core Hindley-Milner type system, especially type classes (Section 6)."
"Section 6.4 Higher-kinded polymorphism The first major, unanticipated development in the type-class story came when Mark Jones, then at Yale, suggested parameterising a class over a type constructor instead of over a type, an idea he called constructor classes (Jones, 1993). The most immediate and persuasive application of this idea was to monads ... Jones’s paper appeared in 1993, the same year that monads became popular for I/O (Section 7). The fact that type classes so directly supported monads made monads far more accessible and popular; and dually, the usefulness of monadic I/O ensured the adoption of higher-kinded polymorphism"
The entire Section 7 is called "Monads and input/output"
Section 7.3 "Monadic I/O Although Wadler’s development of Moggi’s ideas was not directed towards the question of input/output, he and others at Glasgow soon realised that monads provided an ideal framework for I/O"
As a Haskell novice I think I could be forgiven for having the perception that the evolution of monads in Haskell was driven by the unwieldyness of managing mutable state (such as I/O) in a pure lazy language.
https://research.microsoft.com/en-us/um/people/simonpj/paper...
But solving the IO problem neatly is a consequence of the value of the abstraction. It solves it so well because of its useful properties.
I'd say you should take another crack at haskell sometime. But if you're learning Erlang now, stick with that for the time being. It's also got plenty to teach.
A monad is a container type with at least these three operations:
1. unit(x): Create a container with a single element x inside.
2. map(f,c): Given a container c with elements, call the function f on each element, and create a new container out of the results.
3. flatten(c): Given a container of containers, we can flatten it down by putting all the elements in the nested containers in a single container.
For example, a List<T> is a monad:
1. unit(x) creates a list with a single element.
2. map(f,list) calls f on each element of the list, and creates a new list out of the results.
3. flatten(listoflists) flattens the nested lists down. Note that flatten only flattens away one level, so if you have a list of lists of lists, then flattening it will get you a list of lists.
These operations have to have some laws, like:
1. map(f, unit(x)) = unit (f x): if we map a function over a container with a single element, we just call the function on that single element and put it inside its own container.
2. flatten(unit(c)) = c: if we wrap a container c in its own single element container unit(c) then flattening it gives us back the original.
3. flatten(map(unit,c)) = c: if we take each element of a container and wrap each one in its own container, and then flatten the result, we get back the original.
Turns out that you can also define these operations for sets and multisets. The interesting thing is that there are more things that fit in the API above. Futures, parsers, probability distributions, I/O operations, mutable state, dynamic scoping, exceptions, optional values, logic programming, continuations, asynchronous programming, and more. Monads are what all those things have in common. I think the only way, ultimately, to learn how monads work is to study many examples. You may ask yourself, for example, what does it mean to flatten a parser? Turns out you can give a meaningful definition for flattening a parser. If you study many such examples, you start to get the pattern.
What makes monads so neat to use is that you can define special syntax for it. For example list comprehensions can be translated to the operations above, which means that list comprehensions also work on all the other things that are monads. So you get "parser comprehensions" and "futures comprehensions". Similarly, mutable state has special concise syntax in imperative languages, but with monads that syntax also works for lists, probability distributions, etc. That's what do-notation is in Haskell.
I hope this helps somebody get interested in what monads really are about.
That said, to use I/O in Haskell you do not need to understand any of this. If you can read this:
do print "What's your name?"
name <- readLn
print ("Hello, " ++ name)
Then you're good to go. You do not need to know that there is a I/O monad underneath this, you can just do like you're coding in an imperative language. The only difference is that the syntax above is not restricted to I/O and mutable state like it is in most languages, but it can also be used for creating parsers, manipulating lists, etc.For example, the assignment operator in the I/O monad means assignment, but in the list monad it means "take each element of":
do x <- [1,2,3]
y <- [4,5,6]
return (x*y)
This creates the list [1*4,1*5,1*6, 2*4,2*5,2*6, 3*4,3*5,3*6].That would be Audrey Tang. The Perl6 on Haskell project is called Pugs.
* http://en.wikipedia.org/wiki/Audrey_Tang
* https://news.ycombinator.com/user?id=audreyt
It's possible to use combinator parsers (e.g. Parsec) to do everything regexes can do (plus the extras), although regexes are available if desired.
http://stackoverflow.com/a/2704795/4525
I get it now, so thanks!
You say that, but your post doesn't actually show disagreement. He said to avoid analogies, not avoid real code. His list of steps in fact contains "use monads in real code".
Whether it's Haskell or some other purely functional language, I'm of the opinion that every programmer should have one in their toolset.
http://learnyouahaskell.com/chapters
This is good too, but I found it somewhat dry:
http://book.realworldhaskell.org/read/
I think that learning Haskell will make you a better programmer. It's really a different way of doing things.
PHP is actually one of those languages, since it gained real support for first-class functions, anonymous functions and closures starting with 5.3 and improving with 5.4. So if you do learn Haskell you'll be able to directly apply some of its ideas to making your PHP more concise, easier to test, and less error prone. Check out the PHP Underscore library to provide some of the infrastructure: http://brianhaveri.github.io/Underscore.php/
Haskell brings a lot more to the table than first class functions, and it may or may not be a good introduction to functional programming for you, but other functional(-ish) languages such as Scala or Clojure (or Scheme & other Lisps) will work well, too. Just give it time. It's worth the effort, but you're not going to magically transform your code into the Unicorn Monad overnight.
If you do PHP you probably do a lot of JavaScript, too, so there is another language that supports getting into functional programming, and there is definitely a lot of interest in moving JavaScript in more classically functional direction. So you won't be blindsided by maps, folds and promises.
I apologize if you already use first class functions like a boss and you were really asking if you needed static type inference. The answer is Maybe.
But there's a real danger that you will not be able to look at your own code the same way again. It can negatively affect your ability to code with your peers.
I'm not kidding. It's a blue pill/red pill kind of thing.
Functional programmers can write better OO code imho.
While FP doesn't solve everything, it does massively reduce the amount of code you write. Where I work, I started writing everything I owned in F#. After a while, I noticed all other new projects were being written in F#, even by people that had previously told me "we are sticking with C#". Why? Because they feel bad having to write loads more code to get the same work done.
Every time I have to work in C#, I'm constantly annoyed about how verbose even the tiniest things are, how much work there is.
Beware, PHP's syntax for lambdas is incredibly verbose (and IIRC there are some odd limits on how you can use them), so if you become "enlightened" then continue to try to apply it in PHP, you are in for a rough ride. Even C# which slightly embraces functional concepts, gets pretty nasty if you write in any sort of real FP style.
How did you deal with concerns (which I assume your manager must have had) about skilling up the rest of your team, and your increasing component of the bus factor? [1]
I tried to make a case for F# for some parts of our codebase and got shot down!
But if you're OK with the impractical, here goes:
Haskell trains you to think about programming in a couple of subtly different ways that change your entire perspective.
(1) Variables are replaced with definitions and values. You probably remember HTTP as stateless and cookies as a sort of informal "patch" for this; similarly it's hard to define state in Haskell. This means that `x = [1, 2, 3];` is allowed but `x[1] = 5;` is not, because the list cannot be directly modified. This does not actually weaken the language because you can always bind new names to new values, and idiomatically you'd often use recursion -- when you want to change a parameter you arrange the code so that you can recurse and call the function again with a new parameter. It's not 100% practical but let's take the Fibonacci numbers, which you might code in PHP like so:
function fibs($n) {
$curr = 0; $next = 1;
for ($i = 0; $i < $n; $i++) {
$tmp = $curr;
$curr = $next;
$next += $tmp;
}
return $curr;
}
In Haskell you could write this itero-recursively as: let fibs n = f n 0 1
where f n curr next = if n < 1 then curr else f (n - 1) (next) (curr + next)
Notice that there's no $tmp and the named variables are now function arguments. You'll find yourself writing PHP more like: function fibs($n, $curr = 0, $next = 1) {
return n < 1 ? $curr : fibs($n - 1, $next, $curr + $next);
}
(2) Haskell is lazy. The above is actually impure because it implements `fibs` as a function when really the Fibonacci numbers are a sequence and should be treated that way. Haskell actually has no problem with defining the infinite list of Fibonaccis as: fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
That's a list just like any other in Haskell, it just happens to be infinite. Or, you can write the infinite list of primes: primes = 2 : filter prime [3..]
where prime k = all (\n -> mod k n /= 0) (takeWhile (\n -> n^2 <= k) primes)
You will miss this a little in other languages; even Python, which can do this with custom generators, gets a little clumsy. Especially, I miss this in Node.js, where the whole language is built around an idea of "callbacks" which wouldn't need to exist if the JavaScript operators could work on deferred computations (sometimes called "promises").(3) Haskell has a type system and explicit pattern-matching. This is a little harder to express: you're used to saying `x = my_object->x` in PHP to avoid saying "x" over and over. Let's say that the object should also have "y" and "z" fields; then you'll have to deal with cryptic errors if your function is ever called with a my_object which does not have a field named "x", but it will be okay if "y" or "z" is not allowed, even though that doesn't actually make sense really!
In Haskell you'll specify in the definition of the argument "Object x y z"; and then x, y, pattern-matching to those respective values. So your functions don't start off with a bunch of "data-extraction" commands; those commands are in the input pattern. You can also define the function when the patterns do not match, because you can define a function several times for different patterns.
The type system can also flag, for example, unsanitized text going into a SQL query and other such things you might have missed, so you can lean on it a surprising amount.
(4) The biggest thing that this page was talking about was monads, and the reason why it was talking about them is because Haskell offers a special notation for dealing with monads.
A monad is basically a context which you can't necessarily "break the data out of". For example, the "IO" monad is a context which says "this data depends on an unrepeatable IO action, like getting a random number or reading a file." You can't really erase that fact from the data, but that fact shouldn't stand in the way of handling the data. The IO monad allows you to very cleanly separate the stuff which might be different in two different runs of the program, from the stuff which is never so.
The author makes the claim that analogies are bad for understanding monads. Instead, you should just look at the definition. A monad is a typeclass which wraps another type, so the type is "M(T)" where T is any other type. A monad is defined by two functions. One is called "return" and has type T -> M(T), it puts plain data into the monad. The other function is called "bind" and accepts a function T -> M(T2), which intuitively "takes the data and feeds it to the function." The reason that you can't always "break out" of the monad is that the output of this function is M(T2), so the function output must also be in the monad. We know that "monad" is a scary-sounding word but really nobody has a better name for it.
Lists are a good monad; here `return x = [x]` and `bind xs f = concat (map f xs)`. Another simple monad is called Maybe, which can either be `Just x` or `Nothing`, it sort of represents a computation which might fail. Here `return x = Just x` and `bind Nothing fn = Nothing; bind (Just x) fn = fn x`. There's also a "State" monad, which gives you a "put" and "get" method and a scope block for typed modifiable variables, so you can have code that looks imperative -- but you've got to carefully mark it as such with the type system as a whole. The whole point being that once you allow that "put" statement you make it much harder to think about bugs because the value of the parameter will depend on where in the function that parameter appears -- so the type system acts as a warning flag.
http://channel9.msdn.com/Series/C9-Lectures-Erik-Meijer-Func...
http://stackoverflow.com/questions/1012573/getting-started-w...
Personally, I would agree with the guy that said that Learn You a Haskell and Real World Haskell are some of the best resources. LYAH is really good at explaining the language features while RWH is currently the best we have in terms of giving practical examples with real libraries and things like that.
should be
"Inheritance couples code and data together in unnatural ways. Typeclasses are a better way of structuring logic."
What is the value of -ddump-simpl as shown here? http://dev.stephendiehl.com/hask/#%2816%29
I don't know why he is doing all of this though. The slides themselves don't give much context...
i think the yesod o'reilly book (free on yesodweb.com) is a very good one if you dont mind to make web apps.