Writing a counter function
Jon Fairbairn
Jon.Fairbairn@cl.cam.ac.uk
Sun, 30 Jun 2002 13:06:47 +0100
Mark Carrol wrote:
> The beauty of his request was that it was so simple and seemed to make
> sense;
But there's the rub. It's not beautiful and it doesn't make
much sense. I really wish we could get away from the "How do
I convert this imperative code snippet into Haskell"
questions into "How do I solve this abstract problem?"
Take the perl:
##############
sub counter
{
my $a =3D shift;
my $next =3D sub {
my $to_add =3D shift ;
return counter($to_add+$a);
};
return ($a, $next);
}
my ($result,$next) =3D counter(5);
my ($result2, $next2) =3D $next->(100);
my ($result3, $next3) =3D $next2->(50);
my ($result4, $next4) =3D $next->(30);
print "\$result=3D$result\n\$result2=3D$result2\n\$result3=3D$result3\n\$r=
esult4=3D$result4\n";
###########
Now, an extensionally equal Haskell programme is the following:
main =3D putStr "$result=3D5\n\
\$result2=3D105\n\
\$result3=3D155\n\
\$result4=3D35\n"
Like the original programme, it takes no inputs and produces
a certain output. You might well object that this programme
isn't /intentionally/ equal to the one given, but then I
can't work out what the intention was!
I guess that the last "$next" on the last line should have
been "$next3", but I'm not certain, and I certainly have no
idea what the programme is /for/.
The point of Haskell is not that it is easy to write
programmes. On that metric, assembler isn't bad. I don't
find it particularly hard to write assembler
programmes. What I do find hard is reading them, and
determining whether they are what I intended. The big
advantages of Haskell are that it prevents you from writing
certain kinds of programme, and makes it easier to write
certain other kinds.
None of the responses so far has focussed on the problem
with the kind of function defined in the Perl above, namely
that one tends to make slips in passing the correct next
state to the function. One way round this is to use a
monad:
import Single_State
main =3D putStrLn result where
result =3D runS (do result1 <- set_state 5
result2 <- step_counter 100
result3 <- step_counter 50
result4 <- step_counter 30
return $ "result1 =3D "++show result1
++"\nresult2 =3D "++show result2
++"\nresult3 =3D "++show result3
++"\nresult4 =3D "++show result4)
0
step_counter:: Int -> Single_State Int Int
step_counter n =3D do a <- get_state
set_state (n+a)
(Single_State is a silly example at <URL:
http://www.cl.cam.ac.uk/~jf/Haskell/Monad_Examples/Single_State.lhs
>) I don't claim that this is particularly well written, in
particular I'm not sure about what the initial state should
be. Should the above programme be =
result =3D runS (do result1 <- get_state; . . .)
5
for example? Would it be better to give the initial state
as the first argument of runS?
But at least the intermediate states have disappeared from
the source, so there's no chance of using the wrong one.
Yet I still don't know what problem we are supposed to be
addressing!
J=F3n
-- =
J=F3n Fairbairn Jon.Fairbairn@cl.cam.ac.uk
31 Chalmers Road jf@cl.cam.ac.uk
Cambridge CB1 3SZ +44 1223 570179 (after 14:00 only, please!)