现实世界 Haskell 的哪些部分已经过时或被认为是不好的做法?

真实世界 Haskell的第19章中,由于 Control.Exception的改变,很多例子都失败了。

这让我觉得也许这本书中的一些东西实际上已经过时了,不再值得研究,毕竟已经过去了6年。我唯一的另一个参考是《向你学习》 Haskell For Great Good 虽然是一本很棒的书,但是比起 RWH 来,它要基础得多

任何以前读过这本书的人,能否就其中哪些部分不再相关提出一些建议?特别是本书后半部分的章节,例如,软件事务内存、并发编程、套接字编程等。

编辑: 这是关于2008年12月出版的这本书的版本,这是目前为止唯一已知的版本(2017年11月)

10887 次浏览

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 ABC0 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

Streaming libraries

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