When I first learned Haskell, Haskell '98 was the official published language specification. Today, that specification is Haskell 2010. (I have to admit, I have a really hard time remembering what the heck the differences actually are.)
Now Haskell has been around for a long time. (Well, in computing terms it's a long time.) So what I'm wondering is... Have there been any major design changes to the language over Haskell's history? Or have all the changes been relatively minor? Is there somewhere I can find a list of these, without sitting down and reading through every version of the Haskell Report trying to spot the differences?
The history of the language, including major milestones and design decisions, is described in
A History of Haskell: being lazy with class.
#INPROCEEDINGS{Hudak07ahistory,
author = {Paul Hudak and John Hughes and Simon Peyton Jones and Philip Wadler},
title = {A history of Haskell: Being lazy with class},
booktitle = {In Proceedings of the 3rd ACM SIGPLAN Conference on History of Programming Languages (HOPL-III},
year = {2007},
pages = {1--55},
publisher = {ACM Press}
}
The reference Dons gives is excellent and authoritative up to when it ends. Here's some stuff off the top of my head -- which includes things that made into the spec as well as things that aren't officially in the spec but which I'd consider non-experimental parts of GHC that other compilers also often aim to provide. This also includes typeclasses and other features we now consider standard but which weren't always so, but which can exist purely as libraries.
Hierarchical Modules
Monads
The IO Monad
Do notation
The Foreign Function Interface
Multi-parameter type classes
Imprecise exceptions
Typeable and Data
Extensible Exceptions
Functional Dependencies
Type Functions
Concurrent Haskell
STM
GADTs
The Great Monomorphism Catastrophe (i.e. loss of monad comprehensions, map specialized to lists, etc.)
Applicative and Traversable
Arrows/Arrow Notation
MonadFix
Related
In the chapter 19 of Real World Haskell a lot of the examples now fail due to the change of Control.Exception.
That makes me think maybe some of the stuff in this book is actually obsolete and not worth studying anymore, after all it's been 6 years. My only other reference is Learn You a
Haskell For Great Good, though it's a great book, it's much more basic compared with RWH.
Can anyone who have read the book before please give some advice on which parts of it are no longer relevant? Especially the chapters in the second half of the book, for example, software transactional memory, concurrent programming, socket programming, etc.
EDIT: This is about the edition of the book that's published on Dec 2008, which is the only known edition as of today (Nov 2017)
Main issue of RWH
It's old. RWH was written at a time version 6.8 of GHC was being used. 6.8 used base version 3.0.x.x. 6.10.1 already used 4.0.0.0, which introduced many changes. And that's just the jump from 6.8 to 6.10. The current version of GHC is 7.10. Monads have been changed. There's currently a discussion to remove return from Monad, so the Monad instance in Real World Haskell will really be out of sync with the real world.
That being said, it's still a useful resource for general guidelines. But keep in mind that many libraries changed since its release.
Something you can read along while reading RWH is "What I Wish I Knew When Learning Haskell" by Stephen Diehl. It provides additional insight, but be aware, some sections aren't really newcomer friendly.
General remarks
Read the comments. They usually contain information whether the given paragraph/section is still relevant and/or working.
Read the documentation of the libraries/functions you want to use. Even if you're lazy, know at least the types.
Remarks to chapters
This is just a quick overview of some of the things that I noticed while reading RWH. It's probably incomplete.
Chapter 2. Types and Functions vs the FTP
Since GHC 7.10.
The type of null has been changed due to the Foldable-Traversable-Proposal. Many other functions such as foldr, foldl and many other that were previously only defined for [a] in the Prelude have been replaced with more general Foldable t => t a variants.
Chapter 11. Testing and quality assurance
Since Haskell-platform 2010 or late 2008.
Although this is mentioned in a footnote, the QuickCheck library has changed in many ways from version 1 to version 2. For example, generate now uses Gen a instead of StdGen, and the functionality of the old generate is in Test.QuickCheck.Gen.unGen.
In doubt, check the documentation.
Chapter 14. Monads & Chapter 15. Programming with monads
Code breaking: Applicative m => Monad m
As of GHC 7.10, Applicative is now a superclass of Monad, something that wasn't planned in 2007.
In GHC 7.10, Applicative will become a superclass of Monad, potentially breaking a lot of user code. To ease this transition, GHC now generates warnings when definitions conflict with the Applicative-Monad Proposal (AMP).
See 7.8.1 release notes.
The State/Writer/Reader monads
In the Will the real state monad please stand up? section, the authors claim
In order to define a Monad instance, we have to provide a proper type constructor as well as definitions for (>>=) and return. This leads us to the real definition of State.
-- file: ch14/State.hs
newtype State s a = State
runState :: s -> (a, s)
}
That's no longer true, because State and its friends are now implemented via
type State s = StateT s Identity
type Writer w = WriterT w Identity
type Reader r = ReaderT r Identity
So they're defined by their monad transformer.
Chapter 17. Interfacing with C: the FFI
The overall chapter is fine, but as one can read in the comments or on Yuras Shumovich's blog, the finalizer part in the following code is bad practise:
pcre_ptr <- c_pcre_compile pattern (combineOptions flags) errptr erroffset nullPtr
if pcre_ptr == nullPtr
then do
err <- peekCString =<< peek errptr
return (Left err)
else do
reg <- newForeignPtr finalizerFree pcre_ptr -- release with free()
return (Right (Regex reg str))
As malloc() should be used with free(), new with delete, allocate with deallocate, one should always use the correct function.
TL;DR You should always free memory with the same allocator that allocated it for you.
If a foreign function allocates memory, you should also use the accompanying deallocation function.
Chapter 19. Error handling
Error handling changed completely from 6.8 to 6.10, but you noticed that already. Better read the documentation.
Chapter 22. Extended Example: Web Client Programming
Some of the example seem to be broken. Also, there are other HTTP libraries available.
Chapter 25. Profiling and optimization
General profiling techniques are still the same, and the example (see below) is a great case study for problems that can occur in your program. But RWH is missing multi-threaded profiling, e.g. via ThreadScope. Also, lazy IO isn't concerned throughout the whole book, as far as I know.
mean :: [Double] -> Double
mean xs = sum xs / fromIntegral (length xs)
Chapter 24 & Chapter 28 (Concurrent and parallel programming & STM)
While Chapter 24. Concurrent and multicore programming and Chapter 28. Software transactional memory are still relevant, Simon Marlow's book Parallel and Concurrent Programming in Haskell focuses solely on concurrent and parallel programming and is pretty recent (2013). GPU programming and repa are completely missing in RWH.
Chapter 26. Advanced library design: building a Bloom filter
As with the other chapters, the general guidelines of the design library is still well written and relevant. However, due to some changes (?) concerning ST, the result cannot be compiled anymore.
Chapter 27. Network programming
It's still mostly up to date. After all, network programming doesn't change so easily. However, the code uses deprecated functions bindSocket and sClose, which should be replaced by bind and close (preferably via qualified import). Keep in mind that it's very low-level, you might want to use a more specialized high-level library.
Appendix A. Installing GHC and Haskell libraries
GHC 6.8 was the last version before the Haskell Platform has been introduced. Therefore, the appendix tells you to get GHC and Cabal by hand. Don't. Instead, follow the instructions on the haskell.org download page.
Also, the appendix doesn't tell you about Cabal sandboxes, which were introduced in Cabal 1.18 and free you from dependency hell. And of course, stack is missing completely.
Missing content
Some topics are not discussed in RWH at all. This includes streaming libraries such as pipes and conduit, and also lenses.
There are several resources out there for those topics, but here are some links to introductions to give you an idea what they're about. Also, if you want to use vectors, use the vectors package.
Control.Applicative
RWH uses Control.Applicative's (<$>) at several points, but doesn't explain Control.Applicative at all. LYAH and the Typeclassopedia contain sections on Applicative. Given that Applicative is a superclass of Monad (see above), it's recommended to learn that class by heart.
Furthermore, several operators of Control.Applicative (and the typeclass itself) are now part of the Prelude, so make sure that your operators don't clash with <$>, <*> and others.
Lenses
Video by Edward Kmett (author of lens)
Video by Adam Gundry "Lenses: compositional data access and manipulation"
Introduction and tutorial by Jakub Arnold
Streaming libraries
Conduit Overview by Michael Snoyman (author of conduit)
Pipes tutorial by Gabriel Gonzales (author of pipes, included in the pipes package)
Tooling
version 1.18 of Cabal, which introduced sandboxes
stack, a cross-platform program for developing Haskell projects
ghc-mod, a backend for vim, emacs, Sublime Text and other editors
New/missing language extensions and GHC changes
runtime type polymorphism (:i ($) has changed tremendously)
-XTypeInType
-XDataKinds
-XGADT
-XRankNTypes
-XGenericNewtypeDeriving
-XDeriveFunctor
any other extension that happened after 6.6
I'm trying to gain a really deep understanding of the Monad hierarchy of classes. Part of that is, of course, seeing lots of examples, but I'm particularly interested in the history of how these classes were first discovered and their motivations.
I understand that Monads came about initially as a solution to the IO problem in Haskell, and am familiar with papers by Moggi and Wadler in 1989-92 that introduced them.
I've also seen where Applicatives were introducted in Conor McBride and Ross Paterson's "Applicative Programming with Effects."
By my question is what popularized Functors, and when did they come about? I assume it must be after Monad since Functor is not a superclass, but haven't people been using generalized map functions since the early days of LISP?
Functor was in the 1.3 version of the Haskell report which is what standardized both monadic IO and higher kinded type classes. So, the Functor typeclass is as old as the monad typeclass. And, what is more, Functor is the first motivating example in Jones' paper introducting "constructor classes!" You might say the Jones paper popularized the idea, but as you point out people have wanted generalized maps for a long time indead.
I've come across references to Haskell's Data.Typeable, but it's not clear to me why I would want to use it in my code.
What problem does it solve, and how?
Data.Typeable is an encoding of an well known approach (see e.g. Harper) to implementing delayed (dynamic) type checking in a statically typed language -- using a universal type.
Such a type wraps code for which type checking would not succeed until a later phase. Rather than reject the program as ill-typed, the compiler passes it on for runtime checking.
The style originated in Abadi et al., and developed for Haskell by Cheney and Hinze as a wrapper to represent all dynamic types, with the Typeable class appearing as part of the SYB work of SPJ and Lammel.
Reference
Martín Abadi, Luca Cardelli, Benjamin Pierce and Gordon Plotkin, "Dynamic Typing in a Statically Typed Language", ACM Transactions on Programming Languages and Systems (TOPLAS), 1991.
James Cheney and Ralf Hinze, "A lightweight implementation of generics and dynamics", Haskell '02: Proceedings of the 2002 ACM SIGPLAN Workshop on Haskell, 2002.
Lammel, Ralf and Jones, Simon Peyton, "Scrap your boilerplate: a practical design pattern for generic programming, TLDI '03: Proceedings of the 2003 ACM SIGPLAN International Workshop on Types in Languages Design and Implementation, 2003
Harper, 2011, Practical Foundations for Programming Languages.
Even in the text books: dynamic types (with typeable representations) are statically typed languages with only one type, Harper ch 20:
20.4 Untyped Means Uni-Typed
The untyped λ-calculus may be faithfully embedded in a
typed language with recursive types. This means that every
untyped λ-term has a representation as a typed expression
in such a way that execution of the representation of a
λ-term corresponds to execution of the term itself. This
embedding is not a matter of writing an interpreter for
the λ-calculus in ℒ{+×⇀µ} (which we could surely do), but
rather a direct representation of untyped λ-terms as typed
expressions in a language with recursive types.
The key observation is that the untyped λ-calculus is
really the uni-typed λ-calculus! It is not the absence
of types that gives it its power, but rather that it has
only one type, namely the recursive type
D = µt.t → t.
It's a library that allows, among other things, naming types. If a type a is declared Typeable, then you can get its name using show $ typeOf x where x is any value of type a. It also features limited type-casting.
(This is somewhat similar to C++'s RTTI or dynamic languages' reflection.)
One of the earliest descriptions I could find of a Data.Typeable-like library for Haskell is by John Peterson from 1992: http://www.cs.yale.edu/publications/techreports/tr1022.pdf
The earliest "official" paper I know of introducing the actual Data.Typeable library is the first Scrap Your Boilerplate paper from 2003: http://research.microsoft.com/en-us/um/people/simonpj/Papers/hmap/index.htm
I'm sure there's lots of intervening history that someone here can chime in with!
The Data.Typeable class is used primarily for generic programming in the Scrap Your Boilerplate (SYB) style. See also Data.Data
The idea is that SYB defines a collection combinators for performing operations such as printing, counting, searching, substiting, etc in a uniform manner over a variety of user-created types. The Typeable typeclass provides the necessary plumbing.
In modern GHC, you can just say deriving Data.Typeable when defining your own type in order to provide it with the necessary instances.
UML is a standard aimed at the modeling of software which will be written in OO languages, and goes hand in hand with Java. Still, could it possibly be used to model software meant to be written in the functional programming paradigm? Which diagrams would be rendered useful given the embedded visual elements?
Is there a modeling language aimed at functional programming, more specifically Haskell? What tools for putting together diagrams would you recommend?
Edited by OP Sept 02, 2009:
What I'm looking for is the most visual, lightest representation of what goes on in the code. Easy to follow diagrams, visual models not necessarily aimed at other programmers. I'll be developing a game in Haskell very soon but because this project is for my graduation conclusion work I need to introduce some sort of formalization of the proposed solution. I was wondering if there is an equivalent to the UML+Java standard, but for Haskell.
Should I just stick to storyboards, written descriptions, non-formalized diagrams (some shallow flow-chart-like images), non-formalized use case descriptions?
Edited by jcolebrand June 21, 2012:
Note that the asker originally wanted a visual metphor, and now that we've had three years, we're looking for more/better tools. None of the original answers really addressed the concept of "visual metaphor design tool" so ... that's what the new bounty is looking to provide for.
I believe the modeling language for Haskell is called "math". It's often taught in schools.
Yes, there are widely used modeling/specification languages/techniques for Haskell.
They're not visual.
In Haskell, types give a partial specification.
Sometimes, this specification fully determines the meaning/outcome while leaving various implementation choices.
Going beyond Haskell to languages with dependent types, as in Agda & Coq (among others), types are much more often useful as a complete specification.
Where types aren't enough, add formal specifications, which often take a simple functional form.
(Hence, I believe, the answers that the modeling language of choice for Haskell is Haskell itself or "math".)
In such a form, you give a functional definition that is optimized for clarity and simplicity and not all for efficiency.
The definition might even involve uncomputable operations such as function equality over infinite domains.
Then, step by step, you transform the specification into the form of an efficiently computable functional program.
Every step preserves the semantics (denotation), and so the final form ("implementation") is guaranteed to be semantically equivalent to the original form ("specification").
You'll see this process referred to by various names, including "program transformation", "program derivation", and "program calculation".
The steps in a typical derivation are mostly applications of "equational reasoning", augmented with a few applications of mathematical induction (and co-induction).
Being able to perform such simple and useful reasoning was a primary motivation for functional programming languages in the first place, and they owe their validity to the "denotative" nature of "genuinely functional programming".
(The terms "denotative" and "genuinely functional" are from Peter Landin's seminal paper The Next 700 Programming languages.)
Thus the rallying cry for pure functional programming used to be "good for equational reasoning", though I don't hear that description nearly as often these days.
In Haskell, denotative corresponds to types other than IO and types that rely on IO (such as STM).
While the denotative/non-IO types are good for correct equational reasoning, the IO/non-denotative types are designed to be bad for incorrect equational reasoning.
A specific version of derivation-from-specification that I use as often as possible in my Haskell work is what I call "semantic type class morphisms" (TCMs).
The idea there is to give a semantics/interpretation for a data type, and then use the TCM principle to determine (often uniquely) the meaning of most or all of the type's functionality via type class instances.
For instance, I say that the meaning of an Image type is as a function from 2D space.
The TCM principle then tells me the meaning of the Monoid, Functor, Applicative, Monad, Contrafunctor, and Comonad instances, as corresponding to those instances for functions.
That's a lot of useful functionality on images with very succinct and compelling specifications!
(The specification is the semantic function plus a list of standard type classes for which the semantic TCM principle must hold.)
And yet I have tremendous freedom of how to represent images, and the semantic TCM principle eliminates abstraction leaks.
If you're curious to see some examples of this principle in action, check out the paper Denotational design with type class morphisms.
We use theorem provers to do formal modelling (with verification), such as Isabelle or Coq. Sometimes we use domain specific languages (e.g. Cryptol) to do the high level design, before deriving the "low level" Haskell implementation.
Often we just use Haskell as the modelling language, and derive the actual implementation via rewriting.
QuickCheck properties also play a part in the design document, along with type and module decompositions.
Yes, Haskell.
I get the impression that programmers using functional languages don't feel the need to simplify their language of choice away when thinking about their design, which is one (rather glib) way of viewing what UML does for you.
I have watched a few video interviews, and read some interviews, with the likes of erik meijer and simon peyton-jones. It seems as when it comes to modelling and understanding ones problem domain, they use type signatures, especially function signatures.
Sequence diagrams (UML) could be related to the composition of functions.
A static class diagram (UML) could be related to type signatures.
In Haskell, you model by types.
Just begin with writing your function-, class- and data signatures without any implementation and try to make the types fit. Next step is QuickCheck.
E.g. to model a sort:
class Ord a where
compare :: a -> a -> Ordering
sort :: Ord a => [a] -> [a]
sort = undefined
then tests
prop_preservesLength l = (length l) == (length $ sort l)
...
and finally the implementation ...
Although not a recommendation to use (as it appears to be not available for download), but the HOPS system visualizes term graphs, which are often a convenient representation of functional programs.
It may be also considered a design tool as it supports documenting the programs as well as constructing them; I believe it can also step through the rewriting of the terms if you want it to so you can see them unfold.
Unfortunately, I believe it is no longer actively developed though.
I realize I'm late to the party, but I'll still give my answer in case someone would find it useful.
I think I'd go for systemic methodologies of the likes of SADT/IDEF0.
https://en.wikipedia.org/wiki/Function_model
https://en.wikipedia.org/wiki/Structured_Analysis_and_Design_Technique
Such diagrams can be made with the Dia program that is available on both Linux, Windows and MacOS.
You can a data flow process network model as described in Realtime Signal Processing: Dataflow, Visual, and Functional Programming by Hideki John Reekie
For example for code like (Haskell):
fact n | n == 0 = 1
| otherwise = n * fact (n - 1)
The visual representation would be:
What would be the point in modelling Haskell with Maths? I thought the whole point of Haskell was that it related so closely to Maths that Mathematicians could pick it up and run with it. Why would you translate a language into itself?
When working with another functional programming language (f#) I used diagrams on a white board describing the large blocks and then modelled the system in an OO way using UML, using classes. There was a slight miss match in the building blocks in f# (split the classes up into data structures and functions that acted upon them). But for the understanding from a business perspective it worked a treat. I would add mind that the problem was business/Web oriented and don't know how well the technique would work for something a bit more financial. I think I would probably capture the functions as objects without state and they should fit in nicely.
It all depends on the domain your working in.
I use USL - Universal Systems Language. I'm learning Erlang and I think it's a perfect fit.
Too bad the documentation is very limited and nobody uses it.
More information here.
I first came across exceptions with ADA 83. As far as I know, the designers of ADA invented the concept of exceptions. Is this true, or did any programming language that came before use exceptions too?
According to c2.com's Ground Breaking Languages page it was PL/I.
It depends on how you define generics. Parametric polymorphism - which allows you to define functions and types that are not tied to particular argument / field types - was there in ML already - and that's 1973. There is a Standard ML sample from Wikipedia:
fun reverse [] = []
| reverse (x::xs) = (reverse xs) # [x]
Note that this function is statically typed, but polymorphic ("generic") on any type of list.
While this example is SML (which is a later thing), so far as I know, the concept was present in earliest ML versions as well.
From Wikipedia:
Generic programming facilities first
appeared in the 1970s in languages
like CLU and Ada, and were
subsequently adopted by many
object-based and object-oriented
languages, including BETA, C++, D,
Eiffel, Java, and DEC's now defunct
Trellis-Owl language. Implementations
of generics in languages such as Java
and C# are formally based on the
notion of parametricity, due to John
C. Reynolds.