I've been gradually learning Haskell, and even feel like I've got a hang of monads. However, there's still a lot of more exotic stuff that I barely understand, like Arrows, Applicative, etc. Although I'm picking up bits and pieces from Haskell code I've seen, it would be good to find a tutorial that really explains them wholly. (There seem to be dozens of tutorials on monads.. but everything seems to finish straight after that!)
Here are a few of the resources that I've found useful after "getting the hang of" monads:
As SuperBloup noted, Brent Yorgey's Typeclassopedia is indispensable (and it does in fact cover arrows).
There's a ton of great stuff in Real World Haskell that could be considered "after monads": applicative parsing, monad transformers, and STM, for example.
John Hughes's "Generalizing Monads to Arrows" is a great resource that taught me as much about monads as it did about arrows (even though I thought that I already understood monads when I read it).
The "Yampa Arcade" paper is a good introduction to Functional Reactive Programming.
On type families: I've found working with them easier than reading about them. The vector-space package is one place to start, or you could look at the code from Oleg Kiselyov and Ken Shan's course on Haskell and natural language semantics.
Pick a couple of chapters of Chris Okasaki's Purely Functional Data Structures and work through them in detail.
Raymond Smullyan's To Mock a Mockingbird is a fantastically accessible introduction to combinatory logic that will change the way you write Haskell.
Read Gérard Huet's Functional Pearl on zippers. The code is OCaml, but it's useful (and not too difficult) to be able to translate OCaml to Haskell in your head when working through papers like this.
Most importantly, dig into the code of any Hackage libraries you find yourself using. If they're doing something with syntax or idioms or extensions that you don't understand, look it up.
Regarding type classes:
Applicative is actually simpler than Monad. I've recently said a few things about it elsewhere, but the gist is that it's about enhanced Functors that you can lift functions into. To get a feel for Applicative, you could try writing something using Parsec without using do notation--my experience has been that applicative style works better than monadic for straightforward parsers.
Arrows are a very abstract way of working with things that are sort of like functions ("arrows" between types). They can be difficult to get your mind around until you stumble on something that's naturally Arrow-like. At one point I reinvented half of Control.Arrow (poorly) while writing interactive state machines with feedback loops.
You didn't mention it, but an oft-underrated, powerful type class is the humble Monoid. There are lots of places where monoid-like structure can be found. Take a look at the monoids package, for instance.
Aside from type classes, I'd offer a very simple answer to your question: Write programs! The best way to learn is by doing, so pick something fun or useful and just make it happen.
In fact, many of the more abstract concepts--like Arrow--will probably make more sense if you come back to them later and find that, like me, they offer a tidy solution to a problem you've encountered but hadn't even realized could be abstracted out.
However, if you want something specific to shoot for, why not take a look at Functional Reactive Programming--this is a family of techniques that have a lot of promise, but there are a lot of open questions of what the best way to do it is.
Typeclasses like Monad, Applicative, Arrow, Functor are great and all, and even more great for changing how you think about code than necessarily the convenience of having functions generic over them. But there's a common misconception that the "next step" in Haskell is learning about more typeclasses and ways of structuring control flow. The next step is in deciding what you want to write, and trying to write it, exploring what you need along the way.
And even if you understand Monads, that doesn't mean you've scratched the surface of what you can do with monadically structured code. Play with parser combinator libraries, or write your own. Explore why applicative notation is sometimes easier for them. Explore why limiting yourself to applicative parsers might be more efficient.
Look at logic or math problems and explore ways of implementing backtracking -- depth-first, breadth-first, etc. Explore the difference between ListT and LogicT and ChoiceT. Take a look at continuations.
Or do something completely different!
Far and away the most important thing you can do is explore more of Hackage. Grappling with the various exotic features of Haskell will perhaps let you find improved solutions to certain problems, while the libraries on Hackage will vastly expand your set of tools.
The best part about the Haskell ecosystem is that you get to balance learning surgically precise new abstraction techniques with learning how to use the giant buzz saws available to you on Hackage.
Start writing code. You'll learn necessary concepts as you go.
Beyond the language, to use Haskell effectively, you need to learn some real-world tools and techniques. Things to consider:
Cabal, a tool to manage dependencies, build and deploy Haskell applications*.
FFI (Foreign Function Interface) to use C libraries from your Haskell code**.
Hackage as a source of others' libraries.
How to profile and optimize.
Automatic testing frameworks (QuickCheck, HUnit).
*) cabal-init helps to quick-start.
**) Currently, my favourite tool for FFI bindings is bindings-DSL.
As a single next step (rather than half a dozen "next steps"), I suggest that you learn to write your own type classes. Here are a couple of simple problems to get you started:
Writing some interesting instance declarations for QuickCheck. Say for example that you want to generate random trees that are in some way "interesting".
Move on to the following little problem: define functions /\, \/, and complement ("and", "or", & "not") that can be applied not just to Booleans but to predicates of arbitrary arity. (If you look carefully, you can find the answer to this one on SO.)
You know all you need to go forth and write code. But if you're looking for more Haskell-y things to learn about, may I suggest:
Type families. Very handy feature. It basically gives you a way to write functions on the level of types, which is handy when you're trying to write a function whose parameters are polymorphic in a very precise way. One such example:
data TTrue = TTrue
data FFalse = FFalse
class TypeLevelIf tf a b where
type If tf a b
weirdIfStatement :: tf -> a -> b -> tf a b
instance TypeLevelIf TTrue a b where
type If TTrue a b = a
weirdIfStatement TTrue a b = a
instance TypeLevelIf FFalse a b where
type If FFalse a b = b
weirdIfStatement FFalse a b = a
This gives you a function that behaves like an if statement, but is able to return different types based on the truth value it is given.
If you're curious about type-level programming, type families provide one avenue into this topic.
Template Haskell. This is a huge subject. It gives you a power similar to macros in C, but with much more type safety.
Learn about some of the leading Haskell libraries. I can't count how many times parsec has enabled me to write an insanely useful utility quickly. dons periodically publishes a list of popular libraries on hackage; check it out.
Contribute to GHC!
Write a haskell compiler :-).
Related
Control.Category.Constrained is a very interesting project that presents the class for cartesian closed categories - Curry.
Yet, I do not see why we think of all cartesian closed categories which allow curry and uncurry (Hom(X * Y, Z) ≅ Hom(X, Z^Y) in terms of category theory). Wikipedia says that such property holds only for locally small cartesian closed categories. Under this post many people suggest that Hask itself is not locally small (on the other hand, everyone says that Hask is not a cartesian closed category, which I reckon to be pure and uninteresting formalism).
In this post on Math.SE speaks on assuming all categories are locally small. But it is given from a mathematical point of view where we discuss properties. I would like to know why we decided to concentrate on curry and uncurry as Curry’s methods. Is it because pretty much everyone who knows Haskell also knows these functions? Or is there any other reason?
I would like to know why we decided to concentrate on curry and uncurry as Curry’s methods. Is it because pretty much everyone who knows Haskell also knows these functions?
As the library author I can answer that with confidence and the answer is yes: it is because curry and uncurry are well-established part of the Haskell vernacular. constrained-categories was never intended to radically change Haskell and/or make it more mathematically solid in some sense, but rather to subtly generalise the existing class hierarchies – mostly to allow defining functors etc. that couldn't be given Prelude.Functor instances.
Whether Curry could be formalised in terms of local smallness I frankly don't know. I'm also not sure whether that and other “maths foundations” aspects can even be meaningfully discussed in the context of a Haskell library. Somewhat off-topic rant ahead It's just a fact that Haskell is a non-total language, and yes, that means just about any axiom can be thwarted by some undefined attack. But I also don't really see that as a problem. Many people seem to think of Haskell as a sort of uncanny valley: too restrictive for use in real-world applications, yet nothing can be proved properly. I see it exactly the other way around: Haskell has a sufficiently powerful type system to be able to express the mathematical ideas that are useful for real-world applications, without getting its value semantics caught up too deep in the underlying foundations to be practical to actually use in the real world. (I.e., you don't constantly spend weeks proving some “obviously it's true that...” theorem. I'm looking at you, Coq...)Instead of writing 100% rigorous proofs, we narrow down the types as best as possible and then use QuickCheck to see whether something typically works as the maths would demand.
Don't get me wrong, I think formalising the foundations is important too and dependently-typed total languages are great, but all that is somewhat missing the point of where Haskell's potential really lies. At least it's not where I aim my Haskell development, including constrained-categories. If somebody who's deeper into the pure maths wants to chime in, I'm delighted to hear about it.
In Control.Lens we have Getter that can access the nested structure. Getter has use and uses, but it's not clear to me how they work. So it'd be great if someone can provide some simple examples that use or uses is utilised.
Why do I need to know it? because I'm reading some implementation in Haskell and "uses" and "use" were used in them. In particualr it says:
inRange <- uses fsCurrentCoinRangeUpperBound (coinIndex <=)
If the above code is just for comparing (<=) two values, then why do we need "uses" at all, there?
As I tried to make clear in my answer to your other question over at Use cases of makePrisms with examples there is a lot of requisite knowledge required before you can understand this.
First up, you have to understand Lens quite well. Judging from your other question you're just beginning them. This is great! They're amazingly cool and it's excellent to tackle such things.
However, I'd give you a big amount of caution here, one of the dangers of Haskell is it's so powerful, and can be so expressive and terse, that it seems easy to try to skip stuff.
If you skipped understanding algebraic data types very well, for example, you can easily read code and think you have an understanding of it when you don't at all. This can then lead to compounded confusion, and you'll feel like you don't understand any of it, which actually might be true, but that feeling is not a good feeling to have when learning Haskell.
I don't want you to feel like that.
So I encourage you to learn Lens, but if you don't have the requisite knowledge for Lens, then I encourage you to go get that first. It's not too hard to understand this stuff to a degree, but the way Lens is written is not trivial or easy to approach for programmers who aren't quite familiar with at least simple types, parameterized types, Algebraic Data Types, Typeclasses, the Functor typeclass, and to really understand it, you need to understand several instances of Functor.
As well, if you're trying to understand use and uses, which only make sense when dealing with State values, then I'd suggest it's almost impossible to understand what's happening without knowing what State is, as well as what Lens does and is.
use and uses are for taking a lens and a state value and looking into the current state inside a State value. So, to a degree you really need to also understand what do syntax is doing, therefore the Monad typeclass to a degree, as well as how the State / MonadState work from that perspective.
If any of these preliminaries are skipped, you'll be confused.
I hope this helps! And I wish you well.
Say i'm creating an image processing library with Haskell.
I've defined a few of my own data types.
When should i declare some of my data types to be Monad(or functor or applicative functor etc) ?
And what is the benefit of doing so?
I understand that if some data types can be "mapped over" then i can declare it to be an instance of functor. But if i do so what is the benefit ?
This might be a really silly question but i'm still struggling my way into the functional programming realm.
The point is basically the same as the point of using any useful abstract interface in OO programming; all the code that already exists that does useful things in terms of that interface now works on your new type.
There's nothing that says you have to make anything an instance of Monad that could be, and it won't enable you to really do anything you couldn't do anyway. But if you don't, it's practically guaranteed that some of the code you write will in fact be re-implementing things that are equivalent to existing code that works on any monad; probably you will do so without realising it.
If you're not very familiar/confident with the Monad interface, recognising this redundancy and removing it will probably be more effort than just writing the repeated code. But if you do gain that familiarity, then spotting things that could be Monads becomes fairly natural, as does spotting code you were about to write that could be replaced by existing Monad code. Again, this is pretty similar to generically useful abstract interfaces in OO languages; mainstream OO languages just tend to lack the type system features necessary to express the concept of general monads, so it's not one that many OO programmers have already gotten familiar with. As with anything in programming, the best way to gain that familiarity is to just work with it for a while, and stumble through that period where everything takes longer than doing it some other way you're already comfortable with.
Monad is nothing but a very generally useful interface. Monads are particularly useful in Haskell because they have been accepted by the community as standard, so lots of existing library code works with them. But there's really nothing more magical to them than that.
I'm in the process of learning Haskell, and type classes seem like a powerful way to make type-safe polymorphic functions. But a lot of the Haskell Prelude functions don't use them. More specifically:
Most of the list functions don't work with other data structures (for instance, foldr and length are only implemented for lists and can't be used on arrays).
Modules like Data.ByteString are unusable unless you use import qualified since they include functions that have the same names as Prelude functions.
It seems like both of these problems would go away if the standard library used generic functions with type classes (please let me know if I'm totally off base with this).
I have two questions:
Are there technical or design
reasons that the Prelude is like this, or is it just for
historical reasons?
Looking around, it looks like there are a
couple of libraries (like
Data.Foldable and, if I'm not
mistaken, Scrap Your Boilerplate)
that replace the standard Prelude functions
with generic alternatives. Are
there any plans to incorporate these ideas into future versions of Haskell?
There is a very good pragmatic reason that "standard" Haskell (Prelude + base + maybe some more) doesn't use more polymorphism:
Designing general-use type classes is hard. Good designs for classes that abstract over container types like lists, arrays and "bytestrings" (personally I don't really consider Bytestring a container) aren't floating round waiting to be included in Haskell 2012. There are some designs e.g Listlike and the Edison classes, and a number of people have chipped away at the problem but excepting Foldable and Traversable no-one has produced any compelling designs.
The Haskell base library used to be more polymorphic - list comprehensions used to work for any monad, map and ++ weren't limited to List, and perhaps other things.
But folks at the time thought that it led to confusing error messages for beginners and that folks who aren't beginners can use the specifically polymorphic versions.
While there are many things in base, and specifically Prelude, that are historic I think any generalization would see plenty of technical push-back. The main issue is speed - if you're function has a type class constraint then you're going to be passing around a dictionary for the type class functions and maybe eating more space for specialization.
Some of the libraries, such as SYB, use extensions that aren't part of Haskell. The first task would be to formalize and build support for these features. Look at the Haskell' documents to see where Haskell is going and how you might be able to influence that path.
Real World Haskell has some insights about this in the Monad Transformers chapter:
In an ideal world, would we make a break from the past, and switch over Prelude to use Traversable and Foldable types? Probably not. Learning Haskell is already a stimulating enough adventure for newcomers. The Foldable and Traversable abstractions are easy to pick up when we already understand functors and monads, but they would put early learners on too pure a diet of abstraction. For teaching the language, it's good that map operates on lists, not on functors.
This question is of course inspired by Monads in Haskell.
wrapping my head around continuation passing style has helped my javascript coding a lot
I would say First-class functions.
In computer science, a programming
language is said to support
first-class functions (or function
literals) if it treats functions as
first-class objects. Specifically,
this means that the language supports
constructing new functions during the
execution of a program, storing them
in data structures, passing them as
arguments to other functions, and
returning them as the values of other
functions. This concept doesn't cover
any means external to the language and
program (metaprogramming), such as
invoking a compiler or an eval
function to create a new function.
Do you want to measure the usefulness in connection with functional-programming itself or programming in general?
In general, the positive experience of functional programming doesn't result from particular techniques but from the way it changes your thinking -
Holding immutable data
Formulating declaratively (recursion, pattern-matching)
Treating functions as data
So I'd say that functional programming is the answer to your question itself.
But to give a more specific answer too, I'd vote for functional abstraction mechanisms like
monads
arrows
continuation-passing-style
zippers
higher-order-functions
generics + typeclasses.
As already said, they are very abstract things on the first view, but once you have understood them, they are extremely cool and valueable techniques to write concise, error-safe and last but not least highly reusable code.
Compare the following (Pseudocode):
// Concrete
def sumList(Data : List[Int]) = ...
// Generic
def sumGeneric[C : Collection[T], T : Num](Data : C) = ...
The latter might be somewhat unintuitive compared with the first definition, but it allows you to work with any collection and numeric type in general!
All in all, many modern (mainstream) languages have discovered such benefits and introduced very functional features like lambda functios or Linq. Having understood these techniques will also improve writing code in this languages.
One from the "advanced" department: Programming with phantom types (sometimes also called indexed types). It's admittedly not a "standard" technique in functional programming but not entirely esoteric either, and it's something to keep your brain busy for awhile (you asked for something difficult, right? ;)).
In a nutshell, it is about parameterizing types to encode and statically enforce certain properties at compile time. One of the standard examples is the vector addition function that statically ensures that given two vectors of length N and M will return a vector of length N+M or otherwise you get a compile-time error. Yes, there are more interesting applications.
These techniques are not quite as useful in C++ as they are in a proper functional programming language, but so far I've managed to sneak some of this stuff in all of my recent projects at work to a varying degree, most recently in a C++ EDSL context where it worked out really well. You don't necessarily have to encode fancy stuff, learning this helped me catching the situations where a few type tags can reduce the verbosity of an EDSL or allowed a cleaner syntax, for example.
Admittedly, the usefulness is somewhat restricted by language support and what you're trying to achieve.
Some starters:
Generic and Indexed Type (slides with some brief applications overview)
Fun with Phantom Types
The Kennedy and Russo paper mentioned in the slides is Generalized Algebraic Data Types
and Object Oriented Programming and puts some of this stuff into the context of C#/Java.
Chapter 3 in Dave Abraham's book C++ Template Metaprogramming is available online as sample chapter and uses these techniques in C++ for dimensional analysis.
A practical FP project using phantom types is HaskellDB.
I would say that Structural typing in OCaml is particularly rewarding.
recursion. Difficult to wrap your head around it at times
The concept of higher-order functions, lambda functions and the power of generic algorithms that are easy to combine were very beneficial for me. I'm always excited when I see what I can do with a fold in haskell.
Likewise my programming in C# has changed a lot (to the better, I hope) since I got into functional programming (haskell specifically).