Paul Graham is collecting canonical accumulator generators at http://www.paulgraham.com/accgen.html , and has Dylan, E, JavaScript, various dialects Lisp, Lua, Rebol, Ruby, Perl, Python and Smalltalk. Could the serious Haskellers comment on this attempt of mine? foo n = do n' <- newIORef n return (\i -> do { modifyIORef n' (i+); readIORef n' }) This is all related to arguments about the benefits of succinctness and expressivity in programming languages. Dominic Cooney -----Original Message----- From: Paul Graham [mailto:pg@archub.org] Sent: Thursday, 13 June 2002 11:17 PM To: dominic@dcooney.com Subject: Re: Haskell accumulator Would this (if the type signature is unnecc) then be the canonical def in Haskell? foo n = do n' <- newIORef n return (\i -> do { modifyIORef n' (i+); readIORef n' }) --Dominic Cooney wrote:
This is a multi-part message in MIME format.
------=_NextPart_000_0001_01C212A8.F2F6A010 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit
If you are still collecting these Revenge of the Nerds accumulators, here it is in literate Haskell:
mkAccum :: (Num a) => a -> IO (a -> IO a) mkAccum n = do n' <- newIORef n return (\i -> do { modifyIORef n' (i+); readIORef n' })
There are a couple of interesting things here; firstly the (Num a) => is an example of Haskell's type classes.
The second is the 'IO' written everywhere. Haskell is pure, and IO is the library-sanctioned state monad. Thus mkAccum and the accumulator itself evaluate to "IO things" since they involve mutable state.
The only other interesting thing about the type signature is that it is completely unnecessary. The compiler infers the most general type. The programmer can write explicit type signatures to narrow the type of a function, or (more commonly) have the compiler provide a sanity check.
'Return' is unusual too-- it wraps things in the IO monad. Ordinarily it is not required, e.g.
incr n = \x -> n + x
Although this could simply be written as a partial application, like:
incr n = (+) n
A whole test program is attached re: the accumulator.
On another note, I am eagerly awaiting the arrival of ANSI Common Lisp from Amazon. I may be converted yet.
Dominic Cooney
------=_NextPart_000_0001_01C212A8.F2F6A010 Content-Type: application/octet-stream; name="accum.lhs" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="accum.lhs"
This can be built with:
ghc -fglasgow-exts -package lang --make accum.lhs -o accum
GHC is available from http://www.haskell.org/ghc .
module Main (main) where
Haskell is pure-- the state of the world is in the IO monad. Hence the accumulator requires these imports.
import IOExts (IORef, newIORef, readIORef, modifyIORef)
The accumulator.
mkAccum :: (Num a) => a -> IO (a -> IO a) mkAccum n = do n' <- newIORef n return (\i -> do { modifyIORef n' (i+); readIORef n' })
Small test program.
main :: IO () main = do acc <- mkAccum 42 x <- acc 1 put x x <- acc 2 put x
Put is a small function that prints something.
put :: (Show a) => a -> IO () put = putStrLn.show ------=_NextPart_000_0001_01C212A8.F2F6A010--
I am thinking one of the following... (the first needs a type signature due to the monomorphism restriction.. i think.) foo :: Num a => a -> a -> a foo = (+) foo n = (n +) foo n i = n + i On Fri, Jun 14, 2002 at 08:29:55AM +1000, Dominic Cooney wrote:
Paul Graham is collecting canonical accumulator generators at http://www.paulgraham.com/accgen.html , and has Dylan, E, JavaScript, various dialects Lisp, Lua, Rebol, Ruby, Perl, Python and Smalltalk.
Could the serious Haskellers comment on this attempt of mine?
foo n = do n' <- newIORef n return (\i -> do { modifyIORef n' (i+); readIORef n' })
This is all related to arguments about the benefits of succinctness and expressivity in programming languages.
Dominic Cooney
-----Original Message----- From: Paul Graham [mailto:pg@archub.org] Sent: Thursday, 13 June 2002 11:17 PM To: dominic@dcooney.com Subject: Re: Haskell accumulator
Would this (if the type signature is unnecc) then be the canonical def in Haskell?
foo n = do n' <- newIORef n return (\i -> do { modifyIORef n' (i+); readIORef n' })
--Dominic Cooney wrote:
This is a multi-part message in MIME format.
------=_NextPart_000_0001_01C212A8.F2F6A010 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit
If you are still collecting these Revenge of the Nerds accumulators, here it is in literate Haskell:
mkAccum :: (Num a) => a -> IO (a -> IO a) mkAccum n = do n' <- newIORef n return (\i -> do { modifyIORef n' (i+); readIORef n' })
There are a couple of interesting things here; firstly the (Num a) => is an example of Haskell's type classes.
The second is the 'IO' written everywhere. Haskell is pure, and IO is the library-sanctioned state monad. Thus mkAccum and the accumulator itself evaluate to "IO things" since they involve mutable state.
The only other interesting thing about the type signature is that it is completely unnecessary. The compiler infers the most general type. The programmer can write explicit type signatures to narrow the type of a function, or (more commonly) have the compiler provide a sanity check.
'Return' is unusual too-- it wraps things in the IO monad. Ordinarily it is not required, e.g.
incr n = \x -> n + x
Although this could simply be written as a partial application, like:
incr n = (+) n
A whole test program is attached re: the accumulator.
On another note, I am eagerly awaiting the arrival of ANSI Common Lisp from Amazon. I may be converted yet.
Dominic Cooney
------=_NextPart_000_0001_01C212A8.F2F6A010 Content-Type: application/octet-stream; name="accum.lhs" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="accum.lhs"
This can be built with:
ghc -fglasgow-exts -package lang --make accum.lhs -o accum
GHC is available from http://www.haskell.org/ghc .
module Main (main) where
Haskell is pure-- the state of the world is in the IO monad. Hence the accumulator requires these imports.
import IOExts (IORef, newIORef, readIORef, modifyIORef)
The accumulator.
mkAccum :: (Num a) => a -> IO (a -> IO a) mkAccum n = do n' <- newIORef n return (\i -> do { modifyIORef n' (i+); readIORef n' })
Small test program.
main :: IO () main = do acc <- mkAccum 42 x <- acc 1 put x x <- acc 2 put x
Put is a small function that prints something.
put :: (Show a) => a -> IO () put = putStrLn.show ------=_NextPart_000_0001_01C212A8.F2F6A010--
_______________________________________________ Haskell mailing list Haskell@haskell.org http://www.haskell.org/mailman/listinfo/haskell
-- --------------------------------------------------------------------------- John Meacham - California Institute of Technology, Alum. - john@foo.net ---------------------------------------------------------------------------
On Thursday 13 June 2002 05:29 pm, Dominic Cooney wrote:
Paul Graham is collecting canonical accumulator generators at http://www.paulgraham.com/accgen.html , and has Dylan, E, JavaScript, various dialects Lisp, Lua, Rebol, Ruby, Perl, Python and Smalltalk.
Could the serious Haskellers comment on this attempt of mine?
foo n = do n' <- newIORef n return (\i -> do { modifyIORef n' (i+); readIORef n' })
This is all related to arguments about the benefits of succinctness and expressivity in programming languages.
This is a typical red herring - a comparison slanted in favor of the language that is preferred. There's no way you're going to get an elegant solution to this challenge, since it requires in-place variable modification. (PG even clarifies that function must perform "incremented by, not plus".) The "right" Haskell solution is a one-liner (as mentioned in another post). Your solution appears sound, but I get the impression that it would only serve to emphasize the "limitations" of a functional approach (as was seen recently in a similarly ridiculous thread on comp.lang.functional). - j -- The river is moving. The blackbird must be flying.
Dominic Cooney wrote:
foo n = do n' <- newIORef n return (\i -> do { modifyIORef n' (i+); readIORef n' })
I'm not sure such a solution should be submitted without a MASSIVE CAVEAT - that this is not pure haskell style. (I'm also not sure that it is really a legal answer since I don't think it can be used anyhere in a program - but I've not mucked around enough with IORefs to be sure.) A state monad could also be used carrying around the value of n and an incrementing function. And I think thats probably closest to the kind of thing needed. But here's another solution-that-aint, the goal is to build a function that returns a pair containing the incremented value and a new function (that uses that new value) to use the next time around. Sure, it means a bit of jiggery-pokery to work right, we need to extract the value and the new function separately, but why not? data FooPair = FP Integer (Integer -> FooPair) incg :: Integer -> Integer -> FooPair incg n = \i -> let j = n+i in (FP j (incg j)) And for convenience : val (FP i _) = i fun (FP _ f) = f It would be nicer to do this without the data declaration defining incg : incg n = \ i -> let j = n+i in (j,incg j) But it makes the type checker cranky. -- jefu -- museum of differential geometry -- mdg.org
Paul Graham is collecting canonical accumulator generators at http://www.paulgraham.com/accgen.html , and has Dylan, E, JavaScript, various dialects Lisp, Lua, Rebol, Ruby, Perl, Python and Smalltalk.
As others have implied, the only correct answer to this is "it's the wrong question". One of the major advantages of functional programming is that you do most things without global variables, because they are a Bad Thing. So an accumulator based on a variable is simply not the appropriate abstraction in most cases. Think about sum: sum = foldl (+) 0 That works by using an accumulator, but Haskell is so expressive you don't even need to mention the fact! In addition the question is underspecified -- what happens, for example if the argument isn't a number? This is not a problem for Haskell, of course, but in allowing the lisp version to throw an exeption he finesses away the advantage of strong typing.
Could the serious Haskellers comment on this attempt of mine?
foo n = do n' <- newIORef n return (\i -> do { modifyIORef n' (i+); readIORef n' })
What strikes me about this, though, is that perhaps the imperative primitives in Haskell aren't quite perfectly designed. It seems to me that modifyIORef ought to return either the IORef of its value. In Algol68 if n IS a REF INT, (n +:= i) has the value a (which will now contain it's previous contents plus i), so it's a time honoured form. If we had such a version (call it modIORef) we could dazzle the blighters with: foo n = fmap (\n' i -> modIORef n' (+i) >>= readIORef) $ newIORef n and if we had one that returned the value (say modReadIORef) foo n = fmap (\n' i -> modReadIORef n' (+i)) $ newIORef n Now, it was the work of moments to define and test (helped by the type system) the above new functions, which shows what expressive power is really about. Jón PS It's perhaps a bit disappointing that one needs a type signature to use foo = fmap (\n' i->modReadIORef n'(+i)) . newIORef -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk
participants (5)
-
Dominic Cooney -
Jeffrey Palmer -
jefu -
John Meacham -
Jon Fairbairn