R. Rengold
R. Rengold

Reputation: 193

Haskell - Function which returns a different value each time it iterates

Say that I have a function:

doesAThing :: Int -> ChangeState

For the purpose of this question it's not especially important what ChangeState is, only that doesAThing needs to take an Int as a parameter and that doesAThing iterates infinitely.

What I want to do is take such a function:

addNum :: [Int] -> Int
addNum [n] = foldl (+) 0 ([n] ++ [n + 1])

and use it for doesAThing. Currently, the function works fine, however it doesn't do what I want it to do - it always returns the same number. The idea is that each time doesAThing iterates, addNum takes whatever its previous output was and uses that as addNum's parameter. I.e. :

First iteration: say that n was set at 0. addNum returns 1 (0 + 0 + 1) and doesAThing uses that to modify ChangeState.

Second iteration: now addNum takes 1 as its parameter, so that it returns 3 (0 + 1 + 2) and doesAThing uses that to modify ChangeState.

Third iteration: now addNum takes 3 as its parameter, returns 7 (0 + 3 + 4) and doesAThing uses 7 to modify ChangeState.

Etc.

Apologies if this is a really noobish question, self-teaching yourself Haskell can be hard sometimes.

Upvotes: 1

Views: 519

Answers (3)

Luis Casillas
Luis Casillas

Reputation: 30227

You have encountered a situations where a programmer who is comfortable with Haskell would likely turn to monads. In particular the state monad, for which there are a few reasonable tutorials online:

I didn't quite understand the "addNum" operation you are trying to describe, and I think there's some flaws with your attempts to define it. For example, the fact that your code always expects a list of exactly one element suggests that there shouldn't be a list or a foldl at all—just take n as an argument and add.

But from your description, I think the following approximates it as best as I can. (This won't be understandable without studying monads and the state monad a little bit, but hopefully it gives you an example to work with in conjunction with other materials.)

import Control.Monad.State

-- Add the previous `addNum` result to the argument.
addNum :: Int -> State Int Int
addNum n = do
  -- Precondition: the previous call to `addNum` used `put`
  -- (a few lines below here) to record its result as the 
  -- implicit state for the `State` monad.
  previous <- get   
  let newResult = previous + n

  -- Here we fulfill the precondition above.
  put newResult
  return newResult

The idea of the State monad is that you have get and put actions such that executing get retrieves the value that was most recently given as argument to put. To actually use addNum you need to do it within the context of a call to a function like evalState :: State s a -> s -> a, which forces you to specify the initial state that will be seen by the very first get in the very first use of addNum.

So, for example, here we use the traverse function to chain calls to addNum on consecutive elements of the list [1..10]. Each call to addNum for each list element will get the newResult value that was put by the call for the previous element. And the 0 argument to evalState means that the very first addNum call gets a 0:

>>> evalState (traverse addNum [1..10]) 0
[1,3,6,10,15,21,28,36,45,55]

If this feels overwhelming, well, for better or worse that's how Haskell feels at first. Keep at it and build up slowly to examples like this one.

Upvotes: 1

MigMit
MigMit

Reputation: 1697

If you want to change something, that means you need mutable state. There are two options. First, you can make current state one of the arguments of your function, and the new state — part of a result. Like addNum :: [Int] -> ([Int], Int). There is a State monad that helps with that, giving an illusion that there actually is some mutable state.

Secondly, you can store your state in an IORef and make your function use IO monad, like addNum :: IORef [Int] -> IO Int. That way you can actually modify the state kept in the IORef. There are other monads that allow the same thing, like ST, which is great if your mutable state is used only locally, or STM, which helps if your application is highly concurrent.

I would strongly recommend the first option though. Don't use IO (or other imperative things) until you absolutely need it.

Upvotes: 1

Ingo
Ingo

Reputation: 36339

What you want is impossible in Haskell, for good reason, see below.

There is a way to iterate over a function, however, by feeding it its own output in the next iteration.

Here is an example:

iterate (\c -> c + 2) 0

This creates the infinite list

[0,2,4,6,....]

Haskell is a pure language, and this means that a function can only access its arguments, constants, other functions and nothing else. Esqecially, there is no hidden state a function can access. Therefore, with the same input, a Haskell function will compute the same output all times.

Upvotes: 0

Related Questions