
[I'm moving my response to the Haskell-cafe mailing list, as it seems more appropriate for this kind of discussion.] Your question seems to touch on the old chestnut of how to reconcile pure functional programs with the inherently procedural aspects of I/O and state management. Most of the richness to which you refer is, to my mind, a fairly esoteric corner of Haskell with which I've had little cause to engage. I sense you may be coming from a perspective similar to mine of a couple of years ago, hence this response. Maybe what follows is already well known to you (some of it is basic stuff), so please accept my apologies if I'm retreading well-worn paths for you. The classical work (c. 1970's) on using functions to describe programs that update machine state models such programs as functions that take a state and return a new state, maybe returning some other values as well. This could get complicated to handle, and along the way functional structures based on monads were invoked to tidy up the housekeeping. (A paper by Simon Peyton-Jones and John Launchbury [1] about handling state in Haskell was useful for me.) Note the central role of higher order functions in dealing with this: a state-monad function represents a computation on some state, not the result of that state; the result of the computation is available only when the function is applied to some initial state. The do-notation is a syntactic sugar to make this easier to write, but (I think) it can sometimes obscure what is going on. Philip Wadler's paper "The Essence of Functional Programming" [3] nicely illustrates how monads can take care of various housekeeping chores. The IO monad can be viewed as a special case that allows a program to access and update state ("the real world") that exists outside the program. So, turning to your specific questions/concerns:
Also, ultimately, I want to be able to save my work and restart the next day (say) picking up the tags where I left off.
Your top-level program must be "in the IO monad" (or: is an expression that describes an IO monad value, which is a computation that interacts with the real world). Thus, it can access yesterdays work, and also your interactive inputs, and compute a new value that is today's work. The IO monad allows you to sequence the various interactions with persistent state and user dialogue, in a purely functional way.
I'm darned if I can see how to do this in a callback without a global variables (and references from other callbacks, by the way).
I would expect the callback function to accept an initial value of the "global" variable(s), and return its new value(s), leaving the calling site to ensure that the new value is used as required. (This interaction may be "hidden" in a monad (e.g. a state monad), and if the "callback" itself involves interaction with a user must use the IO monad.)
I want to use the old value of a tag to compute the new value, in a callback,
I want to access the tag from other callbacks, and
I want to the value to a mutable list from within the callback.
All of these are accommodated by adopting the accept-a-value-and-return-a-new-value perspective. The calling program that invokes the callbacks deals with ensuring that the callbacks have access to the appropriate results from other callbacks. It is my view that use a functional language effectively, it is necessary to think differently about its structure than one would for a conventional imperative program. Global values become parameters and return values; mutable values become input values and new (separate) return values. If you're used to writing imperative programs that do not use global state, but rather use parameters to pass values, the difference isn't always quite as great as might be imagined. For complex values, this sounds horribly inefficient, but because values are pure (not mutable), very high levels of sharing can (sometimes, with good design!) be extensively shared. Thus, a return value may well consist mostly of a copy of the input value, with differences, rather than actually being a complete new copy. (Chris Okasaki's book shows well how to design to obtain these benefits.) Also, because Haskell is lazy, complex structures passed between functions don't necessarily ever exist in their entirety. Sometimes, I think of a data structure as describing a computational traversal through some data, rathert than something that actually exists. (This leads to very elegant expression of things like Prolog search-and-backtrack algorithms.) So the main point of this posting is to say that I think that the "richness" of "implicit parameters, linear implicit parameters, unsafePerformIO, ..." to which you refer is a shoal of red herring, as far as your goals are concerned. I hope this helps. I've added below a couple of links to my web site to stuff that I accumulated while learning Haskell over the past couple of years. #g -- [1] Simon Peyton-Jones and John Launchbury, State in Haskell, linked from: http://research.microsoft.com/Users/simonpj/Papers/papers.html#monads, document at: http://research.microsoft.com/Users/simonpj/Papers/state-lasc.ps.gz, also at: http://www.cse.ogi.edu/~jl/Papers/stateThreads.ps. Introduces monads as a general technique for handling state, and then introduces a concept of "the world" as a state manipulated by IO. I found this to be one of the more illuminating introductions to Monads (which are central to Haskell I/O). [2] Chris Okasaki, "Purely Functional Data Structures", Cambridge University Press, 1998 [3] Philip Wadler, "The essence of functional programming", linked from http://homepages.inf.ed.ac.uk/wadler/topics/monads.html, paper at http://homepages.inf.ed.ac.uk/wadler/papers/essence/essence.ps . [4] http://www.ninebynine.org/Software/Learning-Haskell-Notes.html [5] http://ninebynine.org/Links.html#Programming-Haskell At 15:34 22/11/04 -0800, John Velman wrote:
On Mon, Nov 22, 2004 at 07:27:44PM +0100, Lennart Augustsson wrote:
[snip]
I admit there are proper uses of global variables, but they are very rare. You have not convinced me you have one.
-- Lennart
It's with some trepidation I bring a problem as a total newbie, but I've been obsessed with this and hung up on it ever since I decided a couple of weeks ago to learn Haskell by using it.
Some brief background:
A while back I decided I wanted a simple 'concept mapping' program that would work the way I work instead of the way someone else works. I envisioned a GUI with a canvas and an entry box (TK/tcl). I type a "concept name" into the entry box, and it shows up on the canvas (initially in a slightly randomized position), in a box, with a unique sequenced identifier. The identifier is also used as a canvas tag for the item. Similar input for relations between concepts. I think that's enough description for now.
Initially, I started programming this with PerlTK, but that effort was interrupted for a few weeks. When I got back to it, I decided to do it in Python instead. But that effort also got interrupted for a few weeks. Before I got back to it, I ran across some material on Haskell I've had in my files for a few years, and decided that I'd use this as a vehicle to actually learn Haskell. (This all sounds a bit unfocused, and it is: I'm retired, sometimes describe myself as an ex mathematician or an ex-PhD having spent years in the aerospace industry instead of academia. Anyway, I have both the luxury and lack of focus of no deadlines, no pressure to publish. I hope to use Haskell to further my main hobby of knowledge representation.)
In perl, my labels/tags were very easy:
In the initialization code:
my @taglist = (); my $nextag = "a";
and in the callback for the entry box:
push(@taglist,$nextag); $nextag++;
(With the starting tag of "a" this results in "a",...."z","aa","ab",...)
Also, ultimately, I want to be able to save my work and restart the next day (say) picking up the tags where I left off.
I'm darned if I can see how to do this in a callback without a global variables (and references from other callbacks, by the way).
In looking for a method, I've discovered that Haskell is a lot richer than I thought (or learned when I tinkered with it back in the late '90s ). I've found out about (but don't know how to use properly) implicit parameters, linear implicit parameters, unsafePerformIO, "safe and sound implementation of polymorphic heap with references and updates (Oleg Kiselyov, (http://www.haskell.org/pipermail/haskell/2003-June/011939.html), implicit configurations, phantom types, ...
I've also found warnings against many of these. I'm inclined to try the unsafePerformIO route as being the simplest, and most commonly used, even though perhaps the least haskell-ish. I like implicit configurations, but couldn't begin to say I understand them yet, and it's a bit heavy for a novice.
In a nutshell:
I want to use the old value of a tag to compute the new value, in a callback,
I want to access the tag from other callbacks, and
I want to the value to a mutable list from within the callback.
I'd certainly be interested in doing without global variables, and would appreciate any advice.
(By the way, I'm using Linux, and so far it looks like HTk is my choice for the GUI interface.)
Best,
John Velman _______________________________________________ Haskell mailing list Haskell@haskell.org http://www.haskell.org/mailman/listinfo/haskell
------------ Graham Klyne For email: http://www.ninebynine.org/#Contact