Reputation: 193
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
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 get
s 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
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
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