Monadic vs "pure" style (was: pros and cons of sta tic typing and side effects)

From: Martin Vlk [mailto:vlcak01@tiscali.cz]
http://www-i2.informatik.rwth-aachen.de/Staff/Current/michaelw/sttt-ml-haske ll.pdf This quote from the paper resonated with me: "Also, if imperative elements of a given application were not taken into account during its design but turn out to be necessary later on, often major parts have to be redesigned or (at least) reimplemented, especially because types change significantly. A simple but recurring example is to add printing of status information to an otherwise purely functional algorithm. In the worst case this could result in having to rewrite the algorithm in a monadic style, but also to rewrite its callers (and transitively their callers as well), plus adjusting all type annotations on the way. Even when using opaque accessors to data structures, the required changes cannot necessarily be limited to a single module, but affect large parts of the system." The difficulty seems to be when you want to turn code that was initially "pure" into monadic code: it requires a fairly substantial rewrite. Once in a monadic style, I expect it is much easier to add various monadic/imperative enhancements. I've experienced this recently, where I've converted an algorithm from a purely functional version, using immutable arrays, to a monadic version using destructive arrays. I introduced errors with the conversion, and the unit test suite that I had for the pure version also had to be converted to a monadic style, in order to test the now-monadic code. So having to perform this conversion is clearly undesirable. I'm reminded of Wadler's "Monads for functional programming" (http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/marktoberdorf.pdf ) where he illustrates how easy it is to extend a program written in a mondadic style. So this begs the question: how much should we stick to a purely functional style? Should we advocate the adoption of a more monadic style from the outset, for programmers new to Haskell too? Alistair. ----------------------------------------- ***************************************************************** Confidentiality Note: The information contained in this message, and any attachments, may contain confidential and/or privileged material. It is intended solely for the person(s) or entity to which it is addressed. Any review, retransmission, dissemination, or taking of any action in reliance upon this information by persons or entities other than the intended recipient(s) is prohibited. If you received this in error, please contact the sender and delete the material from any computer. *****************************************************************

On Tue, 2005-08-30 at 11:08 +0100, Bayley, Alistair wrote:
From: Martin Vlk [mailto:vlcak01@tiscali.cz]
http://www-i2.informatik.rwth-aachen.de/Staff/Current/michaelw/sttt-ml-haske ll.pdf
This quote from the paper resonated with me:
"Also, if imperative elements of a given application were not taken into account during its design but turn out to be necessary later on, often major parts have to be redesigned or (at least) reimplemented, especially because types change significantly. A simple but recurring example is to add printing of status information to an otherwise purely functional algorithm. In the worst case this could result in having to rewrite the algorithm in a monadic style, but also to rewrite its callers (and transitively their callers as well), plus adjusting all type annotations on the way. Even when using opaque accessors to data structures, the required changes cannot necessarily be limited to a single module, but affect large parts of the system."
This is often a misconception, that just because you find you need to 'do' something in the middle of your algorithm, that you need to convert it wholly to monadic style. In the above example, it sounds like a better approach might be to keep the algorithm pure but change it slightly so that it returns a list of intermediate results rather than just the final results. That way an outer bit of code in the IO monad can print status information, but the core of the algorithm remains pure. However such a trick is obviously not possible in every case, such as your example of converting some pure array code to use destructive update. (Unless you can make your code into a function from the current state of the array to the new state (perhaps by returning a list of changes) in which case you could partition the code into the pure part and a thin wrapper that does the iteration and actually updates a mutable array) Duncan

On Tue, 30 Aug 2005, Bayley, Alistair wrote:
The difficulty seems to be when you want to turn code that was initially "pure" into monadic code: it requires a fairly substantial rewrite. Once in a monadic style, I expect it is much easier to add various monadic/imperative enhancements.
I've experienced this recently, where I've converted an algorithm from a purely functional version, using immutable arrays, to a monadic version using destructive arrays. I introduced errors with the conversion, and the unit test suite that I had for the pure version also had to be converted to a monadic style, in order to test the now-monadic code. So having to perform this conversion is clearly undesirable.
The disadvantage of pure functional code is certainly the danger of being forced to rewrite it to monadic code in the future. But there is a big advantage of pure functional code: It gives the guarantee about data dependencies to the user. In many cases Haskell provides a pure functional way out of the decision "monadic or pure": You can write your functions in a way that they return intermediate data in some data structure. Then it is easy to pull them out for output.

On Tue, Aug 30, 2005 at 12:55:55PM +0200, Henning Thielemann wrote:
The disadvantage of pure functional code is certainly the danger of being forced to rewrite it to monadic code in the future. But there is a big advantage of pure functional code: It gives the guarantee about data dependencies to the user. In many cases Haskell provides a pure functional way out of the decision "monadic or pure": You can write your functions in a way that they return intermediate data in some data structure. Then it is easy to pull them out for output.
Also, the ability to recognize when something might need to be monadic or that it will always be pure is a skill you eventually learn as you use haskell. I know that the same issue bothered me a lot in the past, but it comes up less and less nowadys. A useful skill is to know the monad template library well. collecting up a list? use MonadWriter instead of manually concatting the list and your code is still pure and often a lot clearer. if you need some other monadic functionality in the future, it is just a matter of changing a type signature or two and applying the right monad transformer. the contention isn't between 'monads vs. pure' it is 'uses IO vs. pure', monads, like many powerful abstractions, are very useful in making pure code more concise, clear and flexible. John -- John Meacham - ⑆repetae.net⑆john⑈
participants (4)
-
Bayley, Alistair
-
Duncan Coutts
-
Henning Thielemann
-
John Meacham