I don't think you can refer to function composition as a design pattern. The syntax for function composition in Haskell is so light that most of the time it's more effort to write in all the arguments explicitly than it is to use composition. Consider
lineLengths = map (length . words) . lines
as compared to
lineLengths text = map length (map words (lines text))
I don't think that monads are a design pattern either (in this I agree with Tony Morris [1]). Monads are a unified method of doing contextual computations. They are far too general to be a design pattern in themselves (how many design patterns do you know that encompass failure, error handling, nondeterminism, probabilistic computation, logging, backtracking search, mutable state, asynchronous computation, continuations, I/O...)
Monads should be more accurately thought of as an interface that is implemented by many types (in Haskell terminology, a class that many types are instances of).
I am not saying that there aren't any design patterns in Haskell - just that function composition and monads are not examples of design patterns. Instead, common design patterns include
* Writing tail-recursive, accumulating helper functions where you'd use a for loop in an imperative language:
sum xs = go 0 xs
where
go result [] = result
go result (x:xs) = go (result + x) xs
* Using strict left folds to accumulate values
sum = foldl' (+) 0
* Using runStateT and its cousins to tame stateful computations
sum xs = execStateT (go xs) 0
where
go [] = return ()
go (x:xs) = modify (+x) >> go xs
* Using a record of functions to encapsulate related functions that might be given different implementations (like a lightweight version of classes)
data Lens a b = Lens { get :: a -> b, set :: a -> b -> a }
* Using libraries like Conduit or Pipes to structure your program as a flow of data through componentized parts.
Then there are 'large scale' design patterns, like
* Structuring your code in three parts: (i) data type declarations, (ii) pure functions operating on the data, (iii) a thin I/O wrapper to glue the pure functions together.
* Using the MonadIO, MonadState, MonadWriter etc classes to keep your code implementation agnostic.
To re-iterate: I agree with you that functional languages (like Haskell) still have design patterns. But I don't think that monads or function composition are good examples of functional design patterns.
Monads should be more accurately thought of as an interface that is implemented by many types (in Haskell terminology, a class that many types are instances of).
I am not saying that there aren't any design patterns in Haskell - just that function composition and monads are not examples of design patterns. Instead, common design patterns include
* Writing tail-recursive, accumulating helper functions where you'd use a for loop in an imperative language:
* Using strict left folds to accumulate values * Using runStateT and its cousins to tame stateful computations * Using a record of functions to encapsulate related functions that might be given different implementations (like a lightweight version of classes) * Using libraries like Conduit or Pipes to structure your program as a flow of data through componentized parts.Then there are 'large scale' design patterns, like
* Structuring your code in three parts: (i) data type declarations, (ii) pure functions operating on the data, (iii) a thin I/O wrapper to glue the pure functions together.
* Using the MonadIO, MonadState, MonadWriter etc classes to keep your code implementation agnostic.
To re-iterate: I agree with you that functional languages (like Haskell) still have design patterns. But I don't think that monads or function composition are good examples of functional design patterns.
[1] http://blog.tmorris.net/