>I am relatively new to Haskell, and I'm using Hugs 1.4.
>
>My my source of programming is Java, with the odd bit of basic thrown in for
>good measure.
>
>Now one of the first things I notice about Haskell is that there don't seem
>to be variables in the same sense that there are in other programming
>languages.
Yes, in the sense that you can't update Haskell variables.
>I want to be able to store a value, or a set of values for use
>through-out various functions.
This is a very general and common issue in functional programming
languages like Haskell. Haskell has a general solution based on
monads, but that is definitely the wrong place to start for a
beginner.
The most obvious and fundamental solution is simply to add new
parameters to a function that needs access to "global variables,"
i.e., to thread the values you need through each function call. This
can get annoying in some circumstances, but it has the huge advantage
that each part of your program has an explicit description of its
dependencies---no hidden parameters.
A related solution is to do the above, and then use the curried form
of the function. For example, say x is a global you've threaded
through f and g. Then in g, you partially apply f to x, and just use
that normally:
f x a b = ...
g x a b c d = ... h a b ... h c d ... where h = f x
Another solution, when your program deals with lists, is to phrase
your program as a fold. The prelude function foldr has signature:
foldr :: (a -> b -> b) -> b -> [a] -> b
It takes three arguments: a function f :: a -> b -> b, a value z :: b,
and a list xs :: [a]. One way of describing its effect is to say that
it replaces each cons node in a list with the function f, and the nil
node with z. So:
foldr f z [a,b,...]
= foldr f z (a : (b : ( ... : [])))
= a `f` (b `f` ( ... `f` z))
Some common examples of functions that can be profitably expressed as
folds are:
sum :: [Int] -> Int
sum xs = foldr (+) 0 xs -- add up all the members of a list
id :: [a] -> [a]
id xs = foldr (:) [] xs -- the identity on lists
map :: (a -> b) -> [a] -> [b]
map f xs = foldr (\y ys -> f y : ys) [] xs -- a familiar list function
(BTW, it is common in Haskell to factor out the argument xs which
appears on both sides of these equations. So one would write:
sum = foldr (+) 0
)
Another way of looking at foldr is as follows. The argument f :: a ->
b -> b takes a value x :: a and a state s :: b, and uses x as input to
calculate a new state s' :: b. Then the argument z :: b of foldr can
be interpreted as an initial state which is successively transformed
using each member of the list as input. For example, in the function
sum above, the state is the running total of the list members (i.e.,
the sum of each suffix of the list).
foldr is polymorphic in the type b of the state, so you can use any
type you want here. You could use a product to store several values
here, or even a record type. So you can think of it as a global store
which exists for the duration of the fold computation only.
BTW, there is also a function foldl which uses the members of a list
in the opposite direction from foldr. There are many other useful
functions dealing with lists in the Prelude. Often, by using these in
combination, you can restate your program in a way that does not
explicitly deal with the issue of global state.
Most datatypes support a fold-like function, so even if you're not
dealing with lists, you can usually easily define the requisite
fold. For example, try defining a fold over binary trees.
--FC