0

I was wondering if it is possible in Haskell to define a function which upon calling gives the next element of an (infinite) list, so for example:

Prelude> func
1
Prelude> func
2

Is it possible to have such a function in Haskell, and if there is, can you give me an example?

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
Coozekoek
  • 41
  • 1
  • 1
  • 6
  • 3
    Usually pure functions should return always the same answer given the same input. So you can only achieve that with a monad. – Willem Van Onsem Jun 01 '17 at 15:25
  • I tried google'ing for how to do it with monads, but I couldn't find very much. Do you have an idea/example of how to do it? – Coozekoek Jun 01 '17 at 15:27
  • 9
    This looks as a bad idea to me. The whole point of programming in a pure functional programming language is to avoid such side effects, and achieve referential transparency. – chi Jun 01 '17 at 15:30

3 Answers3

5

You could do a Stateful thing like this:

{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.State
import Data.List
import Data.Maybe

-- This is not a function! The misleading name func comes from the question text.
func :: MonadState [a] m => m a
func = state (fromJust . uncons)

exampleUsage :: State [Int] (Int, Int)
exampleUsage = do
    x <- func
    y <- func
    return (x, y)

You can try it in ghci:

> evalState exampleUsage [1..]
(1, 2)

However, at a high level, I would suggest rethinking your requirements. func is not very idiomatic at all; simply working with the infinite list directly is generally going to be much clearer, have lower (syntactic) overhead, and lead to better generated code. For example:

exampleUsage' :: [a] -> (a, a)
exampleUsage' (x:y:_) = (x,y)

N.B. this is two lines of code with no extensions or imports, compared to the previous 11 lines of code including a language extension and three imports. Usage is also simplified; you can drop the call to evalState entirely and be done.

> exampleUsage' [1..]
(1, 2)
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • It is also possible to use `IORef` (or similar) for this. Let me know if you are interested in how this would look. – Daniel Wagner Jun 01 '17 at 15:45
  • I agree that in most cases using State for this is silly, and the question as asked does suggest that this is one of those cases. But certainly there are cases where it will be easier to work with the State "function" than working with the infinite list directly. For example, https://stackoverflow.com/q/41941740/625403 is a question similar to this one but in a different context, and my answer https://stackoverflow.com/a/41942347/625403 uses State to keep track of position in the infinite list. – amalloy Jun 01 '17 at 17:14
4

You can use mutable references and the IO monad (or other stateful monad). This can be made rather pretty via partial application:

Prelude> import Data.IORef
Prelude Data.IORef> ref <- newIORef 0
Prelude Data.IORef> let func = readIORef ref >>= \r -> writeIORef ref (r+1) >> return r
Prelude Data.IORef> func
0
Prelude Data.IORef> func
1

Or closer to what you requested:

Prelude Data.IORef> ref2 <- newIORef [0..]
Prelude Data.IORef> let func2 = readIORef ref2 >>= \(x:xs) -> writeIORef ref2 xs >> return x
Prelude Data.IORef> func2
0
Prelude Data.IORef> func2
1
Thomas M. DuBuisson
  • 64,245
  • 7
  • 109
  • 166
2

It sounds like you are looking for something like other languages' Iterator or Generator constructs. If so, this seems like a good use case for the conduit library. Note that there are options (e.g. pipes); however, conduit may be a good starting point for you.

If you are trying to operate only over lists, using the State Monad may be a simpler answer (as Daniel suggests); however, if you are looking for a more general solution, conduit (or the like) may indeed be the answer.

The func you are searching for is therefore most likely the await function.

Here's a simple example -

import Prelude
import Conduit
import Data.MonoTraversable

main :: IO ()
main = runConduit (source .| consume) >>= print

source :: Monad m => Producer m (Element [Integer])
source = yieldMany [0..]

consume :: Monad m => ConduitM i o m (Maybe (i, i))
consume = do
  mx <- await
  my <- await
  return $ (,) <$> mx <*> my

And its output -

λ main
Just (0,1)
pyrospade
  • 7,870
  • 4
  • 36
  • 52