[Haskell-cafe] Mutable and persistent values (was: Top Level TWI's
again)
Graham Klyne
GK at ninebynine.org
Tue Nov 23 04:26:37 EST 2004
[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 at haskell.org
>http://www.haskell.org/mailman/listinfo/haskell
------------
Graham Klyne
For email:
http://www.ninebynine.org/#Contact
More information about the Haskell-Cafe
mailing list