
This is a matter that I genuinely at the present time do not grasp and I am hoping that some of you who are more familiar with the Haskell language may be able to help enlighten me. I feel the question to be an important one. What material benefit does Haskell derive from being a "pure" functional language as opposed to an impure one? Please provide examples as I require instruction. The following is what I believe to be true at the present time. It seems to be that the decision was made because it was a matter of taste under the belief that computer scientists can and often are superstitious and their superstitions can and often do materially interfere with progress. What I am saying is that at the present time perhaps due to my ignorance I am unfamiliar with how this benefits the language in a material sense. It appears to be a philosophical matter, a matter of identity, what Haskell stands for. The sort of decision that Apple computer and Microsoft made not to go down the POSIX road seems relevant. Historically, Apple did not embrace POSIX. Windows continues to stand for Windows, that is the graphical user interface.

On Thu, Dec 10, 2009 at 12:01 PM, John D. Earle
This is a matter that I genuinely at the present time do not grasp and I am hoping that some of you who are more familiar with the Haskell language may be able to help enlighten me. I feel the question to be an important one. What material benefit does Haskell derive from being a "pure" functional language as opposed to an impure one? Please provide examples as I require instruction.
The following is what I believe to be true at the present time. It seems to be that the decision was made because it was a matter of taste under the belief that computer scientists can and often are superstitious and their superstitions can and often do materially interfere with progress. What I am saying is that at the present time perhaps due to my ignorance I am unfamiliar with how this benefits the language in a material sense. It appears to be a philosophical matter, a matter of identity, what Haskell stands for.
The sort of decision that Apple computer and Microsoft made not to go down the POSIX road seems relevant. Historically, Apple did not embrace POSIX. Windows continues to stand for Windows, that is the graphical user interface.
As I understand it it all started with laziness. I don't know if laziness is impossible without purity, but talks and papers tend to say something like "laziness has kept Haskell pure". /M -- Magnus Therning (OpenPGP: 0xAB4DFBA4) magnus@therning.org Jabber: magnus@therning.org http://therning.org/magnus identi.ca|twitter: magthe

At Thu, 10 Dec 2009 12:07:32 +0000, Magnus Therning wrote:
As I understand it it all started with laziness. I don't know if laziness is impossible without purity
More or less. Haskell is a language where anything can be evaluated lazily by default. Unlike say Scheme, where if you want something to be evaluated lazily you have to be explicit about it, in Haskell it's the opposite - if you want something to be evaluated _strictly_ you have to be explicit about it. If you write an impure expression in Haskell (e.g. using unsafePerformIO) the side-effects of it might be executed once, twice, or never, depending on the Haskell implementation in use, the optimisation flags, or even other Haskell code in the system. And the side-effects might be executed at unpredictable times, in an unpredictable order, and only some of them might occur. Now, this might be fine for debugging purposes, where you want to see what is being done under the hood - but for most other side-effects, you really want them to occur in a more controllable way. Hence, to answer John's question, a lazy language _has_ to be pure (apart from the unsafe functions) because laziness (or, to be more technically correct, non-strict evaluation) is incompatible with good control of the side-effects arising from impure code. Both Clean and Haskell deal with side-effects by allowing the programmer to write imperative code in a pure way - in Haskell, by using monads or FRP, and in Clean by using world-passing and uniqueness types. Now, that answer probably suffices for a Haskell beginner. Of course eventually you realise time and space usage is a side-effect, and _that_ needs to be controlled as well... but despite this, I think my answer is basically right. However, that's not the _only_ reason why you might want purity. Even in a strict language, purity makes it easier to reason about code, because you don't have to consider the environment, only the parameters that are passed in. Of course, it gets more complicated with monads, because the Environment monad and the IO monad both give you implicit environments at the level of "running the monadic actions". But even then, it's supposed to be easier to reason about (once you get the hang of it), and I'd broadly agree with this. -- Robin

Most of the discussion centers on the benefits of functional programming and laziness. Haskell is not merely a lazy functional language. It is a pure lazy functional language. I may need to explain what laziness is. Laziness is where you work through the logic in its entirely before acting on the result. In strict evaluation the logic is worked out in parallel with execution which doesn't make complete sense, but it does allow for an architecture that is close to the machine.

On Thu, Dec 10, 2009 at 9:13 AM, John D. Earle
Most of the discussion centers on the benefits of functional programming and laziness. Haskell is not merely a lazy functional language. It is a pure lazy functional language. I may need to explain what laziness is. Laziness is where you work through the logic in its entirely before acting on the result. In strict evaluation the logic is worked out in parallel with execution which doesn't make complete sense, but it does allow for an architecture that is close to the machine.
Just to roil the waters a bit: no programming language can ever hope to be "purely functional", for the simple reason that real computation (i.e. computation involving IO, interactivity) cannot be functional. "Functional programming" is an unfortunate misnomer. On the other hand, languages can be algebraic. The whole point is provability, not function-ness. More generally: judging by the many competing proposals addressing the issue of how to think formally about real computation (just google stuff like hypercomputation, interactive computation, etc.; Jack Copelandhttp://www.hums.canterbury.ac.nz/phil/people/copeland.shtml#articleshas lots of interesting stuff on this) is still an open question. Soare has three essential papers http://www.people.cs.uchicago.edu/%7Esoare/History/on the subject. I guess the moral of the story is that the concepts and the terminology are both still unstable, so lots of terms in common use are rather ill-defined and misleading (e.g. functional programming). Lazyness is just a matter of how one attaches an actual computation to an expression; a better term would be something like "delayed evaluation" or "just-in-time computation". You don't have to work through any logic to have laziness. Just think about how one reads a mathematical text - you need not actually compute subformulae or even analyze them logically in order to work with them. This applies right down to expressions like "2+3" - one probably would compute "5" on reading that, but what about "12324/8353"? You'd leave the computation until you absolutely had to do it - i.e. one would probably try to eliminate it algebraically first. -gregg

Dear Gregg,
You wrote, "Just think about how one reads a mathematical text - you need not actually compute subformulae or even analyze them logically in order to work with them." I hate to have to say this, but do you realize that algebra is concerned with functions among other things and it is the fact that these expressions are functions and not that they are algebraic that gives them this property? Functional programming is not a misnomer. It is called functional programming because you are quite literally working with functions.
Functions have a profound simplifying effect. The truth is as Haskell has demonstrated that much of the complexity in computer programming is artificial and doesn't need to be there. It makes having to prove correctness a lot easier, but that is not the only motivation behind Haskell and other functional programming languages. The push has been getting performance up to a point where the language may be regarded as respectable. This has been a problem that has dogged artificial intelligence languages. It is easier to get a functional language to be efficient compared to a logic language.
This reason for this is that you get the opportunity to work out more of the details as compared to a logic language. In assembler you get to work out every blessed detail to your hearts content or until you drop. That is why assembler has a reputation for being fast. It is the same reason why functional languages are fast comparatively speaking.
From: Gregg Reynolds
Sent: 10 Thursday December 2009 0939
To: John D. Earle
Cc: Haskell Cafe
Subject: Re: [Haskell-cafe] Re: Why?
On Thu, Dec 10, 2009 at 9:13 AM, John D. Earle

On Thu, Dec 10, 2009 at 11:16 AM, John D. Earle
Dear Gregg,
You wrote, "Just think about how one reads a mathematical text - you need not actually compute subformulae or even analyze them logically in order to work with them." I hate to have to say this, but do you realize that algebra is concerned with functions among other things and it is the fact that these expressions are functions and not that they are algebraic that gives them this property?
Hi John, I'd say algebra is mostly concerned with equalities and substitutions and doesn't much care about functions. The equation for a circle is not a function but that doesn't mean we can't use algebra to manipulate it. Algebras might use the concept of function but they are not necessarily _about_ functions. One could even argue that the lambda calculus isn't "about" functions - it's just a calculus, after all. Haskell goes by the rules of that calculus; whether the expressions involved denote functions is not relevant. The computer is just performs symbol calculus. Generally all formal semantics can be reduced to syntactic calculus as far as I can see.
Functional programming is not a misnomer. It is called functional programming because you are quite literally working with functions.
Except when you're not. IO operations are never functions, for example, so IO expressions never denote functions. FP languages do not (and cannot) eliminate the side effectful nature of such operations; the best they can do is what Haskell does, namely circumscribe them so that they are well-behaved and thus can be properly managed (algebraically). But that doesn't make them functions. So for me at least "algebraic" better describes how Haskell works; what you're working with is expressions and you reason about them algebraically. You can do this reliably not because everything is a function but because non-functions are well-behaved.
Functions have a profound simplifying effect.
No argument there. But the expressive power that comes with first-class functions is not what distinguishes Haskell et al. - lots of non-FP languages support them these days.
The truth is as Haskell has demonstrated that much of the complexity in computer programming is artificial and doesn't need to be there. It makes having to prove correctness a lot easier, but that is not the only motivation behind Haskell and other functional programming languages. The push has been getting performance up to a point where the language may be regarded as respectable.
Sure, but that's only possible because the properties of programs are provable, which is what makes it possible to reliably transform a program into an equivalent but more efficient form. -Gregg

On Dec 9, 2009, at 4:00 AM, Robin Green wrote:
At Thu, 10 Dec 2009 12:07:32 +0000, Magnus Therning wrote:
As I understand it it all started with laziness. I don't know if laziness is impossible without purity
More or less.
The S programming language, used for statistics, - is not pure - DOES have lazy evaluation of arguments. (For a free version, see www.r-project.org.) I suspect that the original motivation for this was so that plot(temperature, pressure) could automatically get "temperature" on the horizontal axis and "pressure" on the vertical axis, but it is also used as a lazy evaluation mechanism. In practice, a lot of S code is close to functional.

Magnus, thank you. It at least gives me a lead. I can focus on the
significance of laziness and what role it may have on purity. That the
language is lazy gives me no anxiety as I see laziness as natural. I see
Haskell as having proven that laziness is viable; a language can be lazy and
fast and memory efficient. I am trying to put the puzzle pieces together and
from what I can gather purity may be a perceived outcome of combinatory
logic with Occam's razor. So perhaps the motivation is scientific. Unless
you absolutely must don't go there. Science is an evolutionary process. You
go from Newtonian mechanics to Relativity. Relativity is more complicated
than Newtonian mechanics, but it was proven that the additional complexity
was needed.
--------------------------------------------------
From: "Magnus Therning"
On Thu, Dec 10, 2009 at 12:01 PM, John D. Earle
wrote: This is a matter that I genuinely at the present time do not grasp and I am hoping that some of you who are more familiar with the Haskell language may be able to help enlighten me. I feel the question to be an important one. What material benefit does Haskell derive from being a "pure" functional language as opposed to an impure one? Please provide examples as I require instruction.
The following is what I believe to be true at the present time. It seems to be that the decision was made because it was a matter of taste under the belief that computer scientists can and often are superstitious and their superstitions can and often do materially interfere with progress. What I am saying is that at the present time perhaps due to my ignorance I am unfamiliar with how this benefits the language in a material sense. It appears to be a philosophical matter, a matter of identity, what Haskell stands for.
The sort of decision that Apple computer and Microsoft made not to go down the POSIX road seems relevant. Historically, Apple did not embrace POSIX. Windows continues to stand for Windows, that is the graphical user interface.
As I understand it it all started with laziness. I don't know if laziness is impossible without purity, but talks and papers tend to say something like "laziness has kept Haskell pure".
/M
-- Magnus Therning (OpenPGP: 0xAB4DFBA4) magnus@therning.org Jabber: magnus@therning.org http://therning.org/magnus identi.ca|twitter: magthe

Hello All Paul Hudak's 'Conception, evolution, and application of functional programming languages' gives an account of the motivations, as its only available to ACM members, here are some lengthy quotes: "At least a dozen purely functional languages exist along with their implementations. The main property that is lost when side effects are introduced is referential transparency; this loss in turn impairs equational reasoning..." "Thus the analogy between goto-less programming and assignment-free programming runs deep. When Dijkstra first introduced structured programming, much of the programming community was aghast-how could one do without goto? But as people programmed in the new style, it was realized that what was being imposed was a discipline for good programming not a police state to inhibit expressiveness. Exactly the same can be said of side-effect free programming, and its advocates hope that as people become more comfortable programming in the functional style, they will appreciate the good sides of the discipline thus imposed. "When viewed in this way functional languages can be seen as a logical step in the evolution of imperative languages-thus, of course, rendering them nonimperative. On the other hand, it is exactly this purity to which some programmers object, and one could argue that just as a tasteful use of goto here or there is acceptable, so is a tasteful use of a side effect. Such small impurities certainly shouldn’t invalidate the functional programming style and thus may be acceptable." http://portal.acm.org/ft_gateway.cfm?id=72554&type=pdf The History of Haskell paper has less account of the motivations, but notes some drawbacks, and gives an advantage: "The purity of the language removed a significant technical obstacle to many type-system innovations, namely dealing with mutable state." http://research.microsoft.com/en-us/um/people/simonpj/papers/history-of-hask... "Why purity?" / Why not? Best wishes Stephen

On 10/12/09 12:07, Magnus Therning wrote:
As I understand it it all started with laziness. I don't know if laziness is impossible without purity, but talks and papers tend to say something like "laziness has kept Haskell pure".
This is true, but laziness has its own advantages. Suppose I have two modules, Foo and Bar. Foo generates a data structure which is then consumed by Bar (possibly with some other component calling both of them). In a strict language I have to choose between one of these three options: 1. Foo generates the structure all at once, and some kind of reference is then passed to Bar. This is nice, simple and modular, but it only works for small structures. There can also be a problem if the structure is slow to generate but is only consumed a bit at a time (e.g. by the user interface) because Bar can only start work once Foo has finished the whole thing. You may also find the Foo is doing lots of unnecessary work building bits of the structure that Bar uses only rarely. 2. Foo and Bar are built together in a single module with their functionality interleaved. This means that Foo can build a bit of the structure, have Bar process it, and so on. However it also destroys any modularity the system might have had. If Bar is part of the user interface, for instance, it means that core functionality gets mixed up. 3. Implement some kind of generator object. This takes a lot of code and makes the logic of Foo and Bar harder to follow. For instance the version of Foo from option 1 might have had a loop of the form "foreach i in xs". Now "i" has to be turned into some kind of member variable with logic to move to the next instance. Of course what you are really doing is creating an ad-hoc hand-coded version of lazy evaluation. Different applications are going to require different choices. Worse yet, the right answer can change. For instance you may start with option 1, then discover that the program runs too slowly. Or marketing decide that the maximum size of the data structure has to be increased. So at that point you have to rewrite a big chunk of code. Or else you go with option 2 or 3 because the designer thought it was necessary for efficiency when in fact option 1 would have done nicely. Of course with lazy evaluation you get option 3 all the time automatically. So its just not a problem. This makes the design much simpler because things that previously had to be decided by a human are now decided by the compiler. Paul.

On Thu, Dec 10, 2009 at 12:01 PM, John D. Earle
This is a matter that I genuinely at the present time do not grasp and I am hoping that some of you who are more familiar with the Haskell language may be able to help enlighten me. I feel the question to be an important one. What material benefit does Haskell derive from being a "pure" functional language as opposed to an impure one? Please provide examples as I require instruction.
The killer app for that, IMO, is parallelism these days.In large applications it's very hard to know for sure that a function truly has no side effects, so if the language can actually guarantee it for you then that certainly has immense value if you're trying to run things in parallel. Of course, various forms of lazy processing is becoming popular even in mainstream languages (especially with LINQ etc.), which also requires that the expressions to be pure. Currently mainstream languages rely on programmers being Very Careful, but again these kinds of assumptions aren't scalable. -- Sebastian Sylvan

Hello Sebastian, Thursday, December 10, 2009, 4:27:49 PM, you wrote:
The killer app for that, IMO, is parallelism these days
btw, are you seen Google App Engine? it's python/java ATM, but i think that haskell will be ideal fit there. it's all about computations-in-cloud, or more precisely hosting-in-a-cloud, like Amazon EC2 so, both individual cpus and largest servers now are multi-threaded, it just need some time so EC2/GAP developers will realize Haskell potential -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On Thu, Dec 10, 2009 at 1:38 PM, Bulat Ziganshin
Hello Sebastian,
Thursday, December 10, 2009, 4:27:49 PM, you wrote:
The killer app for that, IMO, is parallelism these days
btw, are you seen Google App Engine? it's python/java ATM, but i think that haskell will be ideal fit there. it's all about computations-in-cloud, or more precisely hosting-in-a-cloud, like Amazon EC2
so, both individual cpus and largest servers now are multi-threaded, it just need some time so EC2/GAP developers will realize Haskell potential
Indeed. Something like Windows Azure could be a good fit too (as it more directly supports native code than GAE does). -- Sebastian Sylvan

On Dec 10, 2:38 pm, Bulat Ziganshin
Hello Sebastian,
Thursday, December 10, 2009, 4:27:49 PM, you wrote:
The killer app for that, IMO, is parallelism these days
btw, are you seen Google App Engine? it's python/java ATM, but i think that haskell will be ideal fit there. it's all about computations-in-cloud, or more precisely hosting-in-a-cloud, like Amazon EC2
I think the first language which would come to mind, is acutally Erlang. As a cloud server process and cloud database backend might be physically distributed, it's more logical to send messages between them asynchronously, than share state in parallel. But yes, Haskell fit's this pattern well, through the Control.Concurrent library.
so, both individual cpus and largest servers now are multi-threaded, it just need some time so EC2/GAP developers will realize Haskell potential
-- Best regards, Bulat mailto:Bulat.Zigans...@gmail.com
_______________________________________________ Haskell-Cafe mailing list Haskell-C...@haskell.orghttp://www.haskell.org/mailman/listinfo/haskell-cafe

2009/12/10 Sebastian Sylvan
The killer app for that, IMO, is parallelism these days.
Parallelism has been a killer app for quite a long time: Darlington's ALICE running Hope: http://www.chilton-computing.org.uk/acd/dcs/projects/p011.htm Clean was originally targeted to parallel machines: http://wiki.clean.cs.ru.nl/FAQ Many, many more: http://www.di.unipi.it/didadoc/lfc/OtherPapers/parallelism/parallel-introduc... http://www.risc.uni-linz.ac.at/people/schreine/papers/pfpbib.ps.gz Best wishes Stephen

John D. Earle wrote:
This is a matter that I genuinely at the present time do not grasp and I am hoping that some of you who are more familiar with the Haskell language may be able to help enlighten me. I feel the question to be an important one. What material benefit does Haskell derive from being a "pure" functional language as opposed to an impure one? Please provide examples as I require instruction.
The following is what I believe to be true at the present time. It seems to be that the decision was made because it was a matter of taste under the belief that computer scientists can and often are superstitious and their superstitions can and often do materially interfere with progress. What I am saying is that at the present time perhaps due to my ignorance I am unfamiliar with how this benefits the language in a material sense. It appears to be a philosophical matter, a matter of identity, what Haskell stands for.
The sort of decision that Apple computer and Microsoft made not to go down the POSIX road seems relevant. Historically, Apple did not embrace POSIX. Windows continues to stand for Windows, that is the graphical user interface.
Laziness, referential transparency, equational reasoning,... They're excellent things, but how about a pragmatic example? I was working recently on a metaprogramming framework to automate the generation of a bunch of shell scripts for wiring programs together. To ensure bug-free scripts we want to maintain a few invariants. One invariant is that the names of files to be generated should not be accessible prior to the file actually being generated (to avoid the file equivalent of a null-pointer dereference). Another invariant is that if someone wants to run a particular generated script then we should ensure that all prerequisite scripts are run first (a la any other build system). After some familiarity with Haskell it's easy to see that what we want here is a monad. This particular monad keeps track for each value (i.e., script) the prerequisite values necessary for it to be valid (i.e., tracking all other values used in constructing this one). The monad laws ensure that no values can escape--- but only with purity. If we could, for example, make global assignments without altering the type signatures then it would be possible to smuggle a file name out of the monadic scope, and thus to violate our invariant about names only being accessible when they're valid. These sorts of issues were painfully apparent because I was writing this framework in an impure language for non-Haskellites to use. There are many other examples along these lines as well. In general, purity means we can actually use mathematical notions in a rigorous way to ensure the program behaves appropriately and are not susceptible to various logic bugs. Referential transparency and equational reasoning are just two special-case examples of this general benefit. Having monads actually be monads is another less recognized one. -- Live well, ~wren

John D. Earle wrote:
What material benefit does Haskell derive from being a "pure" functional language as opposed to an impure one?
1. Code optimisation becomes radically easier. The compiler can make very drastic alterations to your program, and not chance its meaning. (For that matter, the programmer can more easily chop the code around too...) 2. Purity leads more or less directly to laziness, which has several benefits: 2a. Unecessary work can potentially be avoided. (E.g., instead of a function for getting the first solution to an equation and a seperate function to generate *all* the solutions, you can just write the latter and laziness gives you the former by magic.) 2b. You can define brand new flow control constructs *inside* the language itself. (E.g., in Java, a "for" loop is a built-in language construct. In Haskell, "for" is a function in Control.Monad. Just a plain ordinary function that anybody could write.) 2c. The algorithms for generating data, selecting data and processing data can be seperated. (Normally you'd have to hard-wire the stopping condition into the function that generates the data, but with lazy "infinite" data structures, you can seperate it out.) 2d. Parallel computation. This turns out to be more tricky than you'd think, but it's leaps and bounds easier than in most imperative languages. 3. It's much harder to accidentally screw things up by modifying a piece of data from one part of the program which another part is still actually using. (This is somewhat similar to how garbage collection makes it harder to free data that's still in use.) That's at least 6 really huge reasons why purity is a massive win.

C'mon Andrew - how about some facts, references?
2009/12/10 Andrew Coppin
1. Code optimisation becomes radically easier. The compiler can make very drastic alterations to your program, and not chance its meaning. (For that matter, the programmer can more easily chop the code around too...)
Which code optimizations?
From a different point of view, whole program compilation gives plenty of opportunity for re-ordering transformations / optimization - Stalin (now Stalinvlad) and MLton often generated the fastest code for their respective (strict, impure) languages Scheme and Standard ML.
Feel free to check the last page of the report here before replying with the Shootout - (GHC still does pretty well though easily beating Gambit and Bigloo): http://docs.lib.purdue.edu/ecetr/367/
2. Purity leads more or less directly to laziness, which has several benefits:
Other way round, no?
2a. Unecessary work can potentially be avoided. (E.g., instead of a function for getting the first solution to an equation and a seperate function to generate *all* the solutions, you can just write the latter and laziness gives you the former by magic.)
Didn't someone quote Heinrich Apfelmus in this list in the last week or so: http://www.haskell.org/pipermail/haskell-cafe/2009-November/069756.html "Well, it's highly unlikely that algorithms get faster by introducing laziness. I mean, lazy evaluation means to evaluate only those things that are really needed and any good algorithm will be formulated in a way such that the unnecessary things have already been stripped off." http://apfelmus.nfshost.com/quicksearch.html
2b. You can define brand new flow control constructs *inside* the language itself. (E.g., in Java, a "for" loop is a built-in language construct. In Haskell, "for" is a function in Control.Monad. Just a plain ordinary function that anybody could write.)
Psst, heard about Scheme & call/cc?
2c. The algorithms for generating data, selecting data and processing data can be seperated. (Normally you'd have to hard-wire the stopping condition into the function that generates the data, but with lazy "infinite" data structures, you can seperate it out.)
Granted. But some people have gone quite some way in the strict world, e.g.: http://users.info.unicaen.fr/~karczma/arpap/FDPE05/f20-karczmarczuk.pdf
2d. Parallel computation. This turns out to be more tricky than you'd think, but it's leaps and bounds easier than in most imperative languages.
Plenty of lazy and strict, pure and impure languages in this survey: http://www.risc.uni-linz.ac.at/people/schreine/papers/pfpbib.ps.gz
3. It's much harder to accidentally screw things up by modifying a piece of data from one part of the program which another part is still actually using. (This is somewhat similar to how garbage collection makes it harder to free data that's still in use.)
In a pure language I'd like to think its impossible... Best wishes Stephen

On Thu, Dec 10, 2009 at 2:42 PM, Stephen Tetley
C'mon Andrew - how about some facts, references?
Filling in :-)
2009/12/10 Andrew Coppin
: 1. Code optimisation becomes radically easier. The compiler can make very drastic alterations to your program, and not chance its meaning. (For that matter, the programmer can more easily chop the code around too...)
Which code optimizations?
As a very potent example: stream fusion. Here is a talk demonstrating how it works. http://unlines.wordpress.com/2009/10/22/talk-on-loop-fusion-in-haskell/
2a. Unecessary work can potentially be avoided. (E.g., instead of a function for getting the first solution to an equation and a seperate function to generate *all* the solutions, you can just write the latter and laziness gives you the former by magic.)
Didn't someone quote Heinrich Apfelmus in this list in the last week or so:
http://www.haskell.org/pipermail/haskell-cafe/2009-November/069756.html
"Well, it's highly unlikely that algorithms get faster by introducing laziness. I mean, lazy evaluation means to evaluate only those things that are really needed and any good algorithm will be formulated in a way such that the unnecessary things have already been stripped off."
That was me. I love that quote. The half that you omitted is another point on Andrew's list: "But laziness allows to simplify and compose algorithms. Sometimes, seemingly different algorithms turn out to be two sides of the same coin when formulated with lazy evaluation. Isn't it great that finding the k-th minimum is not only an adaption of quicksort but can readily be obtained from it by composing it with (!! k)?"
http://apfelmus.nfshost.com/quicksearch.html
2b. You can define brand new flow control constructs *inside* the language itself. (E.g., in Java, a "for" loop is a built-in language construct. In Haskell, "for" is a function in Control.Monad. Just a plain ordinary function that anybody could write.)
Psst, heard about Scheme & call/cc?
But, very importantly, purity allows you to *restrict* the flow constructs that client code has available. If you have continuations + mutable state you can do anything, but the more code can *do*, the less you *know* about it. For example, providing parser combinators as an applicative functor offers more power than a traditional parser generator, but not enough that we can no longer parse efficiently. Luke

2b. You can define brand new flow control constructs *inside* the language itself. (E.g., in Java, a "for" loop is a built-in language construct. In Haskell, "for" is a function in Control.Monad. Just a plain ordinary function that anybody could write.)
Psst, heard about Scheme & call/cc?
But, very importantly, purity allows you to *restrict* the flow constructs that client code has available. If you have continuations + mutable state you can do anything, but the more code can *do*, the less you *know* about it. For example, providing parser combinators as an applicative functor offers more power than a traditional parser generator, but not enough that we can no longer parse efficiently.
Exactly my fear over unsafePerformIO in 3rd party Haskell libraries :-). One can present an interface in Haskell that looks safe, but it can be very unsafe in its implementation. Dave
Luke _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Thu, Dec 10, 2009 at 6:34 PM, David Leimbach
2b. You can define brand new flow control constructs *inside* the language itself. (E.g., in Java, a "for" loop is a built-in language construct. In Haskell, "for" is a function in Control.Monad. Just a plain ordinary function that anybody could write.)
Psst, heard about Scheme & call/cc?
But, very importantly, purity allows you to *restrict* the flow constructs that client code has available. If you have continuations + mutable state you can do anything, but the more code can *do*, the less you *know* about it. For example, providing parser combinators as an applicative functor offers more power than a traditional parser generator, but not enough that we can no longer parse efficiently.
Exactly my fear over unsafePerformIO in 3rd party Haskell libraries :-). One can present an interface in Haskell that looks safe, but it can be very unsafe in its implementation.
Hear, hear! I always meet with armies of resistance when I say this, but unsafePerformIO should die a horrible, unforgiven death. "Well what if you want blah blah blah with a pure interface?" My response: too fscking bad! If you have a pure interface, great, that means your semantics is pure, which has some nice advantages, but your implementation ain't so bite the bullet. Oh you have a proof that it is well behaved? I doubt it, because you can't prove diddly about IO because it has no specified semantics. You get a lot more from purity than just the interface. Pure code can be freely modulated by evaluation strategy (as long as semantic strictness is preserved), serialized, and verified (in the 100%-certain sort of way) with free theorems. You lose all of that when you allow even supposedly-pure-interface unsafePerformIO. unsafeCoerce, too, of course. Luke

2009/12/10 Luke Palmer
I always meet with armies of resistance when I say this...
The troops arrive.
...but unsafePerformIO should die a horrible, unforgiven death. "Well what if you want blah blah blah with a pure interface?" My response: too fscking bad!
Wouldn't removing `unsafePerformIO` just force us, in many cases, to write the same thing in C and then import it? Or would you amend the FFI so "math.h sin" could not be imported pure? There are plenty of bad ways to use `unsafePerformIO`, this is true; but as we already have a tool for binding to native code in a way that trusts it to be pure, I don't see how having a way to bind to nominally side-effecting Haskell code in a way that trusts it to be pure adds anything to our troubles. -- Jason Dusek

On Dec 11, 2009, at 3:55 PM, Jason Dusek wrote:
There are plenty of bad ways to use `unsafePerformIO`, this is true; but as we already have a tool for binding to native code in a way that trusts it to be pure, I don't see how having a way to bind to nominally side-effecting Haskell code in a way that trusts it to be pure adds anything to our troubles.
Indeed, and I would add that we even trust that our Haskell implementation itself is pure, even though it does all sorts of side-effectful things underneath the hood. Cheers, Greg

On Fri, Dec 11, 2009 at 4:55 PM, Jason Dusek
2009/12/10 Luke Palmer
: I always meet with armies of resistance when I say this...
The troops arrive.
...but unsafePerformIO should die a horrible, unforgiven death. "Well what if you want blah blah blah with a pure interface?" My response: too fscking bad!
Wouldn't removing `unsafePerformIO` just force us, in many cases, to write the same thing in C and then import it? Or would you amend the FFI so "math.h sin" could not be imported pure?
This is not the sort of resistance I expected :-). Naturally my unrealistic argument applies to FFI as well; sin, if imported from C, would have to return in an appropriate structure. Not necessarily IO (I don't like the idea of a universal sin-bin -- I'd rather introduce a family of structures with well-defined meanings), but the same idea. Support for commutative monads (once we understand them) may relieve some of the pain of this approach. The idea being that any code that is pure could be evaluated anywhere with a very simple interpreter. If you have pure code, you can trace it back and evaluate it in a sandbox where you don't need a C runtime, a linker, or really anything but the simplest substitution engine. *All* effects bubble their way up to the top level, so that we know from the type signature of a value the machinery we will need to run it. Pure functional code as the minimal essence of pure computation -- everything else an EDSL. Of course I am not talking about Haskell anymore. Haskell is a firm, maturing language and has no room for such radical changes. This argument applies to an eventual spiritual successor of Haskell (it is probably too liberal for Haskell' even). Luke

Luke Palmer wrote:
The idea being that any code that is pure could be evaluated anywhere with a very simple interpreter. If you have pure code, you can trace it back and evaluate it in a sandbox where you don't need a C runtime, a linker, or really anything but the simplest substitution engine. *All* effects bubble their way up to the top level, so that we know from the type signature of a value the machinery we will need to run
The alternative is not much better: syntactic sugar (say a "wrapping" keyword similar to "deriving") that wraps up a pure type in a State, ST, or IO. The inevitable result is that *every* type from the lazy programmer will be so wrapped. Many programmers overdo the IO monad as it is. With suitable sugar, they will become addicted! Dan

2009/12/11 Luke Palmer
The idea being that any code that is pure could be evaluated anywhere with a very simple interpreter. If you have pure code, you can trace it back and evaluate it in a sandbox where you don't need a C runtime, a linker, or really anything but the simplest substitution engine. *All* effects bubble their way up to the top level, so that we know from the type signature of a value the machinery we will need to run it.
This sounds good but there is something puzzling about it. Where do we draw the line between "machinery" and "packages"? The types don't tell us what libraries we need. They don't tell us how much RAM/CPU we need, either.
Pure functional code as the minimal essence of pure computation -- everything else an EDSL.
Partial or total code? -- Jason Dusek

Quoth Luke Palmer
This is not the sort of resistance I expected :-). Naturally my unrealistic argument applies to FFI as well; sin, if imported from C, would have to return in an appropriate structure. Not necessarily IO (I don't like the idea of a universal sin-bin -- I'd rather introduce a family of structures with well-defined meanings), but the same idea. Support for commutative monads (once we understand them) may relieve some of the pain of this approach.
Yes! I mean, I have no idea, but it seems like it's worth a look at anything that promises to replace some of the unsafePerformIO hacking with more functionally transparent ways to do the same thing. I love talking about the questionable purity of sin! but as it happens, since I don't do that much geometry I am without sin anyway. Donn

Stephen Tetley wrote:
C'mon Andrew - how about some facts, references?
Facts I can do. References? Not so much...
1. Code optimisation becomes radically easier. The compiler can make very drastic alterations to your program, and not chance its meaning. (For that matter, the programmer can more easily chop the code around too...)
Which code optimizations?
In a pure language, substituting a function call with the corresponding RHS is guaranteed to not alter the program. Eliminating common subexpressions is guaranteed not to alter the meaning of the program. Something like stream fusion which replaces a data structure in memory with a loop in the program code is guaranteed not to alter the meaning of the program. Should I continue? On the other hand, turn up the optimisation settings on a C compiler high enough and the program breaks. Somewhere the compiler elides a second call to a function which actually happens to have some side-effect that the compiler isn't aware of, and your stream is now one byte out of alignment, or whatever. Fiddling with optimisation settings in GHC may make your program slow to a crawl rather than go faster, but it *never* makes it segfault on startup.
From a different point of view, whole program compilation gives plenty of opportunity for re-ordering transformations / optimization - Stalin (now Stalinvlad) and MLton often generated the fastest code for their respective (strict, impure) languages Scheme and Standard ML.
I didn't say it's impossible to optimise impure languages. I said it's much harder.
2. Purity leads more or less directly to laziness, which has several benefits:
Other way round, no?
A philosopher once asked: Do we gaze at the stars because we are human? Or are we human because we gaze at the stars? Pointless, really. Is Haskell pure because it's lazy, and being impure in a lazy language would be a hellish nightmare? Or is it lazy because in a pure language, there's no reason to be strict?
2a. Unecessary work can potentially be avoided. (E.g., instead of a function for getting the first solution to an equation and a seperate function to generate *all* the solutions, you can just write the latter and laziness gives you the former by magic.
Didn't someone quote Heinrich Apfelmus in this list in the last week or so:
http://www.haskell.org/pipermail/haskell-cafe/2009-November/069756.html
"Well, it's highly unlikely that algorithms get faster by introducing laziness. I mean, lazy evaluation means to evaluate only those things that are really needed and any good algorithm will be formulated in a way such that the unnecessary things have already been stripped off."
Writing an algorithm custom-optimised to every possible use-case is probably more efficient than using laziness. On the other hand, using laziness is probably radically less typing, and arguably not that much slower. (And then you can invoke the notion of the Sufficiently Sophisticated Compiler...)
2b. You can define brand new flow control constructs *inside* the language itself. (E.g., in Java, a "for" loop is a built-in language construct. In Haskell, "for" is a function in Control.Monad. Just a plain ordinary function that anybody could write.)
Psst, heard about Scheme & call/cc?
No. Should I have?
2c. The algorithms for generating data, selecting data and processing data can be seperated.
Granted. But some people have gone quite some way in the strict world, e.g.:
http://users.info.unicaen.fr/~karczma/arpap/FDPE05/f20-karczmarczuk.pdf
2d. Parallel computation. This turns out to be more tricky than you'd think, but it's leaps and bounds easier than in most imperative languages.
Plenty of lazy and strict, pure and impure languages in this survey: http://www.risc.uni-linz.ac.at/people/schreine/papers/pfpbib.ps.gz
All of these seem to be of the form "you can do this in an impure language". I didn't say you can't. I said it's easier.
3. It's much harder to accidentally screw things up by modifying a piece of data from one part of the program which another part is still actually using. (This is somewhat similar to how garbage collection makes it harder to free data that's still in use.)
In a pure language I'd like to think its impossible...
Agreed. But - unless your program involves no I/O at all - you would be wrong... :-( Haskell programs can and sometimes do involve mutable state internally as well. And in that case, it's up to you to not accidentally do something dumb.

On Fri, Dec 11, 2009 at 11:46 AM, Andrew Coppin
On the other hand, turn up the optimisation settings on a C compiler high enough and the program breaks.
Not if you write actual C as specified by the standard. In fact, these days gcc internally converts your program to SSA form, which is essentially pure functional code. (See here for an explanation: http://www.cs.princeton.edu/~appel/papers/ssafun.ps) The reason is exactly as you say: the purer the code, the easier it is to reason about it. -- Dan

On Thu, Dec 10, 2009 at 4:42 PM, Stephen Tetley
C'mon Andrew - how about some facts, references?
2009/12/10 Andrew Coppin
: 1. Code optimisation becomes radically easier. The compiler can make very drastic alterations to your program, and not chance its meaning. (For that matter, the programmer can more easily chop the code around too...)
Which code optimizations?
Anything permitted by free theorems for one. That gives you a whole lot of code motion possibilities. Purity also gives the compiler freedom to increase sharing. It lets GHC rely on 'almost correct' ways to evaluate thunks on multiple cores with blackholing that has a vanishingly small but still existent race condition, which is FAR faster than the version they would have to use if they were more concerned about accidentally duplicating effects.
From a different point of view, whole program compilation gives plenty of opportunity for re-ordering transformations / optimization - Stalin (now Stalinvlad) and MLton often generated the fastest code for their respective (strict, impure) languages Scheme and Standard ML.
Feel free to check the last page of the report here before replying with the Shootout - (GHC still does pretty well though easily beating Gambit and Bigloo): http://docs.lib.purdue.edu/ecetr/367/
2. Purity leads more or less directly to laziness, which has several benefits:
Other way round, no?
Laziness pushes you in the direction of purity, if only because it provides you with the freedom you need to recover usable efficiency, but I would argue that the converse is also true to some extent. When you are designing a pure language, you can't do as much in a strict setting as you can in a lazy setting. Laziness allows you to tie the knot without mutation -- or rather using only the mutation intrinsic to thunk-evaluation in call-by-need. A pure strict language is also often faced with some kind of fun/val distinction, leading to ml like syntactic warts, and eta-reduction isn't always sound, so EDSLs wind up carrying extra ()'s or need pointful plumbing ala f#'s |>.
2a. Unecessary work can potentially be avoided. (E.g., instead of a function for getting the first solution to an equation and a seperate function to generate *all* the solutions, you can just write the latter and laziness gives you the former by magic.)
Didn't someone quote Heinrich Apfelmus in this list in the last week or so:
http://www.haskell.org/pipermail/haskell-cafe/2009-November/069756.html
"Well, it's highly unlikely that algorithms get faster by introducing laziness. I mean, lazy evaluation means to evaluate only those things that are really needed and any good algorithm will be formulated in a way such that the unnecessary things have already been stripped off."
I mean, lazy evaluation means to evaluate only those things that are really needed and any good algorithm will be formulated in a way such that
Continuing that quotation seems to say quite the opposite. the unnecessary things have already been stripped off. But laziness allows to simplify and compose algorithms. Not only that but the surrounding example is the classic 'take 5 . qsort', which DOES change its asymptotics in the face of laziness. When you are writing the algorithm from scratch, I agree that it is the case that you probably derive nothing from laziness. The nice thing about laziness is that it permits you to avoid this tedium and makes it easier to grab a widget from the standard library and just have it suit your purposes. Another non-intuitive asymptotic changing difference is that laziness provides a side-effect free way to describe memoization and dynamic programming over certain domains. http://apfelmus.nfshost.com/quicksearch.html
2b. You can define brand new flow control constructs *inside* the language itself. (E.g., in Java, a "for" loop is a built-in language construct. In Haskell, "for" is a function in Control.Monad. Just a plain ordinary function that anybody could write.)
Psst, heard about Scheme & call/cc?
To be fair, most scheme control structures are implemented using macros, and call-by-macro-expansion is a form of call-by-name: http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_macro_expansion
2c. The algorithms for generating data, selecting data and processing data can be seperated. (Normally you'd have to hard-wire the stopping condition into the function that generates the data, but with lazy "infinite" data structures, you can seperate it out.)
Granted. But some people have gone quite some way in the strict world, e.g.:
http://users.info.unicaen.fr/~karczma/arpap/FDPE05/f20-karczmarczuk.pdfhttp://users.info.unicaen.fr/%7Ekarczma/arpap/FDPE05/f20-karczmarczuk.pdf
Given. But then people have written some amazing things in brainfuck too. That doesn't mean that I want to subject myself to working in such a sufficient computing environment :) -Edward Kmett

-Edward Kmett
On Mon, Dec 14, 2009 at 2:20 PM, Jason Dusek
2009/12/14 Edward Kmett
: [...] That doesn't mean that I want to subject myself to working in such a sufficient computing environment :)
Sufficient?
That word was meant to be surrounded by big sarcasm quotes. By sufficient I meant that it is Turing complete, and so in some sense sufficient to general purpose programming -- even if it is not a desirable environment in which to code. -Edward Kmett

On Dec 11, 2009, at 1:01 AM, John D. Earle wrote:
This is a matter that I genuinely at the present time do not grasp and I am hoping that some of you who are more familiar with the Haskell language may be able to help enlighten me. I feel the question to be an important one. What material benefit does Haskell derive from being a "pure" functional language as opposed to an impure one? Please provide examples as I require instruction.
It sounds as though you think the Haskell designers wanted a language for some other reason, then cast around and said "what kind of language will it be?" and decided to make it a pure lazy functional one. It's the other way around. The programming language world was full of people trying all sorts of things. It so happened that several groups were exploring techniques for implementing pure lazy functional languages. In a fit of sanity that gives one hope for humanity, they said "why don't we all work on a *common* language?" The big outsider was Clean, which, however, adopted quite a lot of things from Haskell, and now has or is aquiring a Haskell98 front end. (It was already close enough to Haskell that the biggest pain in converting was that 'if' is just a plain function in Clean, not special syntax.) The question to ask, then, is "what material benefit did the pure lazy functional language community get from adopting a common language". Here's one hint: there are several Haskell implementations, numerous Haskell books, and a bulging library of reusable Haskell code. There's basically one out of date Clean book and an unfinished one available on the web. As for why anyone should care about pure functional languages, read "Why Functional Programming Matters". Oddly enough, the W3C language for transforming XML (XSLT) is a pure functional language, and so is the C++ type system.
The sort of decision that Apple computer and Microsoft made not to go down the POSIX road seems relevant.
(1) When the Mac was designed, POSIX did not exist. They _couldn't_ go down the POSIX road. (2) Apple computer *did* go down the POSIX road, or very nearly. MacOS X is as good a UNIX as ever came down the street. It's also a lot *more* than POSIX, but I routinely expect to have no more trouble moving code from say Solaris to MacOS than to Linux; sometimes less. (3) Since the release of Windows NT, Microsoft *have* gone down the POSIX road. I have Microsoft's "Services for Unix Applications" installed on a laptop. What Microsoft don't seem to be very interested in is going down the C99 road. Like Mac OS X, Windows is a lot *more* than POSIX. I suggest that one reason for functional language implementation researchers to concentrate on a *pure* functional language is to keep themselves honest: whenever a performance problem comes up it's no good saying "oh we'll do this the old fashioned imperative way" (as SML and O'Caml do), they have to find a way to do it well _functionally_.

This is a matter that I genuinely at the present time do not grasp and I am hoping that some of you who are more familiar with the Haskell language may be able to help enlighten me. I feel the question to be an important one. What material benefit does Haskell derive from being a "pure" functional language as opposed to an impure one? Please provide examples as I require instruction.
I think the STM monad is one of the greatest examples of why Haskell's approach to side-effects is a resounding success. Without having to change anything to the language and compiler, i.e. as a mere library addition (more or less), you get a concurrency system with optimistic synchronization, where all the needed invariants are trivially enforced by the type-system: - no prints or other un-revertable side effects in transactions. - all accesses to shared variables are protected by a transaction. - ... Monads aren't always perfect when it comes to side-effects, but in the context of STM, they really shine. Stefan

The issue of *purity* in Haskell and this thread has confused me. At value level (not type level) is this linked with *equational reasoning*? Are the operational semantics of Haskell similar but not the same as equational logic? Why are theorem provers such as Haskabelle need? http://www.mail-archive.com/haskell-cafe@haskell.org/msg64843.html Pat

On Friday 11 December 2009 3:24:03 am pbrowne wrote:
The issue of *purity* in Haskell and this thread has confused me.
At value level (not type level) is this linked with *equational reasoning*? Are the operational semantics of Haskell similar but not the same as equational logic?
Why are theorem provers such as Haskabelle need? http://www.mail-archive.com/haskell-cafe@haskell.org/msg64843.html
The use of equational reasoning in a language like Haskell (as advocated by, say, Richard Bird) is that of writing down naive programs, and transforming them by equational laws into more efficient programs. Simple examples of this are fusion laws for maps and folds, so if we have: foo = foldr f z . map g . map h we can deforest it like so: foldr f z . map g . map h = (map f . map g = map (f . g)) foldr f z . map (g . h) = (foldr f z . map g = foldr (f . g) z) foldr (f . g . h) z now, this example is so simple that GHC can do it automatically, and that's a side benefit: a sufficiently smart compiler can use arbitrarily complex equational rules to optimize your program. But, for instance, Bird (I think) has a functional pearl paper on incrementally optimizing a sudoku solver* using equational reasoning such as this. Now, you can still do this process in an impure language, but impurity ruins most of these equations. It is not true that: map f . map g = map (f . g) if f and g are not pure, because if f has side effects represented by S, and g has side effects represented by T, then the left hand side has side effects like: T T T ... S S S ... while the right hand side has side effects like: T S T S T S ... so the equation does not hold. This is all, of course, tweaking programs using equational rules you've checked by hand. Theorem provers are for getting machines to check your proofs. Hope that helps. -- Dan * If you check the haskell wiki page on sudoku solvers, there's an entry something like 'sudoku ala Bird', written by someone else (Graham Hutton, comes to mind), that demonstrates using Bird's method.

From your example, I can appreciate the value of purity. But I am still unsure how close Haskell terms are to pure *equational logic*[1]. The operational semantics of languages like CafeOBJ[1] are very close to
From your point-free example, it seems that the Haskell compiler can
Dan, their intended logical semantics. CafeOBJ modules contain theories in equational logic and evaluations can be considered as proof of these theories. perform *equational reasoning* to transform higher level (point) terms into more efficient basic Haskell code. But my questions concerns the precise semantics of this *basic* Haskell code. Two questions 1) Is this correspondence between operational and logical semantics a desirable property of *purity* in the Haskell world? 2) What, if anything, prevents the execution of a Haskell term from being a proof in equational logic? It seems that Haskells type system has this logical purity[4] My motivation for such questions is that I am researching various *algebraic styles* such as Haskell, OCAML, Maude, and CafeOBJ. My current general research question is; how closely do executable terms in these languages match their *logical* meaning. For example, languages like Java need additional logical scaffolding (like JML[3]) to give them logical machine readable meaning. I *guess* that the gap between operational and logical semantics is rather less in Haskell that in Java, and I further *guess* that the gap less again in Maude or CafeOBJ. I think a good answer to question 2) would remove the *guess* from the Haskell case. Pat [1] http://rewriting.loria.fr/documents/plaisted.ps.gz [2]http://www.forsoft.de/~rumpe/icse98-ws/tr/0202Diaconescu.ps [3] http://www.eecs.ucf.edu/~leavens/JML/ [4]http://www.haskell.org/haskellwiki/Type_arithmetic Dan Doel wrote:
The use of equational reasoning in a language like Haskell (as advocated by, say, Richard Bird) is that of writing down naive programs, and transforming them by equational laws into more efficient programs. Simple examples of this are fusion laws for maps and folds, so if we have:
foo = foldr f z . map g . map h
we can deforest it like so:
foldr f z . map g . map h = (map f . map g = map (f . g)) foldr f z . map (g . h) = (foldr f z . map g = foldr (f . g) z) foldr (f . g . h) z
now, this example is so simple that GHC can do it automatically, and that's a side benefit: a sufficiently smart compiler can use arbitrarily complex equational rules to optimize your program. But, for instance, Bird (I think) has a functional pearl paper on incrementally optimizing a sudoku solver* using equational reasoning such as this.
Now, you can still do this process in an impure language, but impurity ruins most of these equations. It is not true that:
map f . map g = map (f . g)
if f and g are not pure, because if f has side effects represented by S, and g has side effects represented by T, then the left hand side has side effects like:
T T T ... S S S ...
while the right hand side has side effects like:
T S T S T S ...
so the equation does not hold.
This is all, of course, tweaking programs using equational rules you've checked by hand. Theorem provers are for getting machines to check your proofs.
Hope that helps. -- Dan
* If you check the haskell wiki page on sudoku solvers, there's an entry something like 'sudoku ala Bird', written by someone else (Graham Hutton, comes to mind), that demonstrates using Bird's method.

On Fri, Dec 11, 2009 at 10:04 AM, pbrowne
Two questions 1) Is this correspondence between operational and logical semantics a desirable property of *purity* in the Haskell world?
2) What, if anything, prevents the execution of a Haskell term from being a proof in equational logic?
It seems that Haskells type system has this logical purity[4]
My motivation for such questions is that I am researching various *algebraic styles* such as Haskell, OCAML, Maude, and CafeOBJ. My current general research question is; how closely do executable terms in these languages match their *logical* meaning. For example, languages like Java need additional logical scaffolding (like JML[3]) to give them logical machine readable meaning.
I admit that I don't fully know what you are talking about. What do you mean by logical meaning -- as opposed to what other sort of meaning? The equational reasoning in Haskell comes from its denotational semantics (which, mind, is not completely formally specified -- but once you expand typeclasses to dictionary passing style, you get a standard DCPO semantics). If terms are equal in denotation, then they are equal by observation (modulo efficiency). So it is not equations that directly define the semantics of Haskell, but rather mapping the syntax to mathematical objects. We derive Haskell's equational reasoning from the equational reasoning in the semantic domain. That's how I see it as a user of the language. At the most abstract level, omitting some of the practical details of the language (such as the built-in numeric types), Haskell's reduction follows beta reduction with sharing, and so at that level I think the answer to (2) is "nothing". Luke
I *guess* that the gap between operational and logical semantics is rather less in Haskell that in Java, and I further *guess* that the gap less again in Maude or CafeOBJ. I think a good answer to question 2) would remove the *guess* from the Haskell case.
Pat [1] http://rewriting.loria.fr/documents/plaisted.ps.gz [2]http://www.forsoft.de/~rumpe/icse98-ws/tr/0202Diaconescu.ps [3] http://www.eecs.ucf.edu/~leavens/JML/ [4]http://www.haskell.org/haskellwiki/Type_arithmetic
Dan Doel wrote:
The use of equational reasoning in a language like Haskell (as advocated by, say, Richard Bird) is that of writing down naive programs, and transforming them by equational laws into more efficient programs. Simple examples of this are fusion laws for maps and folds, so if we have:
foo = foldr f z . map g . map h
we can deforest it like so:
foldr f z . map g . map h = (map f . map g = map (f . g)) foldr f z . map (g . h) = (foldr f z . map g = foldr (f . g) z) foldr (f . g . h) z
now, this example is so simple that GHC can do it automatically, and that's a side benefit: a sufficiently smart compiler can use arbitrarily complex equational rules to optimize your program. But, for instance, Bird (I think) has a functional pearl paper on incrementally optimizing a sudoku solver* using equational reasoning such as this.
Now, you can still do this process in an impure language, but impurity ruins most of these equations. It is not true that:
map f . map g = map (f . g)
if f and g are not pure, because if f has side effects represented by S, and g has side effects represented by T, then the left hand side has side effects like:
T T T ... S S S ...
while the right hand side has side effects like:
T S T S T S ...
so the equation does not hold.
This is all, of course, tweaking programs using equational rules you've checked by hand. Theorem provers are for getting machines to check your proofs.
Hope that helps. -- Dan
* If you check the haskell wiki page on sudoku solvers, there's an entry something like 'sudoku ala Bird', written by someone else (Graham Hutton, comes to mind), that demonstrates using Bird's method.
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Friday 11 December 2009 1:06:39 pm Luke Palmer wrote:
That's how I see it as a user of the language. At the most abstract level, omitting some of the practical details of the language (such as the built-in numeric types), Haskell's reduction follows beta reduction with sharing, and so at that level I think the answer to (2) is "nothing".
Yes, that's probably the answer, but a related question is how useful the logic is for various applications. For one, the logic that corresponds to Haskell via Curry-Howard is inconsistent, because general recursion/fix/error/negative types/etc. let you prove forall a. a But that isn't a very desirable property from a logical standpoint, because it calls into question whether any proofs you do really prove what they allege. Second, again via Curry-Howard, Haskell's (GHC's) type system corresponds to a higher-order logic without first-order quantification, because it doesn't have real dependent types. This is why you need things like Haskabelle: because the propositions (types) in Haskell *can't* talk about value-level terms, and those are generally what you want to prove things about. With enough GADT/type family fanciness, you can reconstruct (roughly) every value-level entity in the type level, and establish a correspondence between any value-level term 't' and its associated type-level term 'T', such that if you prove something about T, you know (by construction) that a related property holds for t. And then you can use the fact that Haskell is a higher- order logic to prove the things you want about T. But this is convoluted and difficult (and you still have to worry that your proof is accidentally bottom :)). -- Dan

Luke Palmer wrote:
I admit that I don't fully know what you are talking about. What do you mean by logical meaning -- as opposed to what other sort of meaning?
Consider Maude's rewrite logic RWL[1] which has similar inference rules as equational logic(EL), but without the symmetry rule. From [1], the *logical* meaning in RWL regards the rules of rewriting logic as meta-rules for correct deduction in a logical system. Logically, each rewriting step is a logical entailment in a formal system. Note that the particular logic may vary (e.g. EL,FOPL, RWL). [1]http://maude.cs.uiuc.edu/maude1/manual/maude-manual-html/maude-manual_62.htm...

pbrowne wrote:
2) What, if anything, prevents the execution of a Haskell term from being a proof in equational logic?
I have done some checking to try to answer my own question. The answer *seems* to be that there *seems* to be three things that prevent Haskell terms being true equations. Any feedback on these three reason would be welcome. If they are valid reasons, what is their possible impact? Pat --------------------Reason (1)------------------ There are some equations that are not expressible in Haskell. Quoting form: http://www.mail-archive.com/haskell-cafe@haskell.org/msg64843.html Is there any way to achieve such a proof in Haskell itself?
GHC appears to reject equations such has mirror (mirror x) = x mirror (mirror(Node x y z)) = Node x y z
Eugene Kirpichov Fri, 25 Sep 2009 04:19:32 -0700 It is not possible at the value level, because Haskell does not support dependent types and thus cannot express the type of the proposition "forall a . forall x:Tree a, mirror (mirror x) = x", and therefore a proof term also cannot be constructed. Another example from [5]. A partial order that is also linear is called a total order. The class linorder of total orders is specified using the usual total order axioms. Of course, such axiomatizations are not possible in Haskell. --------------------Reason (2)------------------ According to Thompson [1] the equations in Miranda (and I assume Haskell) are not pure mathematical equations due to *where* and other reasons. According to Danielsson [2] the fact that they are not always structurally equations does not prevent functional programmers from using them as if they were valid equations. Danielsson show that this informal *fast and loose* use of equational axioms and theorems is *morally correct*. In particular, it is impossible to transform a terminating program into a looping one. These results justify the informal reasoning that functional programmers use. --------------------Reason (3)------------------ There is no formal specification for the meaning of a Haskell program (i.e. its meaning is not defined in a logic). At the level of precise logical specification and logical interoperability this could be a problem (e.g. semantic web likes logic). This should not be a problem for programming tasks, after all most languages like Java do not have a formal semantic definition in logic (ML, Maude[3] and CafeOBJ[4] do). [1]A Logic for Miranda, Revisited (1994) by Simon Thompson http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.55.149 [2] Fast and Loose Reasoning is Morally Correct - Danielsson, Hughes http://citeseer.ist.psu.edu/733155.html [3] http://maude.cs.uiuc.edu/ [4] http://www.ldl.jaist.ac.jp/cafeobj/ [5]Translating Haskell to Isabelle: Torrini, Lueth,Maeder,Mossakowski http://es.cs.uni-kl.de/TPHOLs-2007/proceedings/B-178.pdf
participants (23)
-
Andrew Coppin
-
Bulat Ziganshin
-
Dan Doel
-
Dan Piponi
-
Dan Weston
-
David Leimbach
-
Donn Cave
-
Edward Kmett
-
Gregg Reynolds
-
Gregory Crosswhite
-
Jason Dusek
-
Johann Höchtl
-
John D. Earle
-
Luke Palmer
-
Magnus Therning
-
Paul Johnson
-
pbrowne
-
Richard O'Keefe
-
Robin Green
-
Sebastian Sylvan
-
Stefan Monnier
-
Stephen Tetley
-
wren ng thornton