Reputation: 159
Recentely I began to study functional programming paradigm using Scala as reference language. I come up with this problem: How to generate a random number with no side effect ? Googling I found this solution:
package fp.crazy-bankers.utils
object Rng {
def next(seed : Int) : (Int,Int) = {
val newSeed = (seed * 0x5DEECE66DL + 0xBL) & 0x FFFFFFFFFFFFL
val number = (newSeed >>> 16).toInt
(newSeed, number)
}
}
Basically here the state (the seed) is managed passing it explicitly on every invocation of next method. This implementation works for me until I call it from the same place because in this way is easy to keep care of the state and pass it on each invocation.
But what if I need a random number in different places? For example if I call next method from 10 different actors, in this case each actor is able to pass its local copy of the state only.
Basically in this way there is no global knowledge of the state across all actors,so risks is different actors get the same random number. How to solve this issue?
Have I to find someway to manage globally the state or try with a completely different pattern?
Upvotes: 1
Views: 745
Reputation: 4587
You can share mutable state between different places using the Ref
construct. There are several implementations of it in Scala:
ZIO Ref: https://zio.dev/docs/datatypes/datatypes_ref
cats-effect Ref: https://typelevel.org/cats-effect/docs/2.x/concurrency/ref
However this requires you to pass the Ref explicitly to every place that is going to use it. This is a feature, not a bug, because it makes it easier to identify all the places where some piece of mutable state is used – there is no way to have global mutable state.
But since you mentioned actors, I doubt that you're really doing pure FP anyway. There's basically no way to do that at least with Akka, because even sending a message between actors is a side effect.
Upvotes: 3
Reputation: 27421
If you want to "use a purely functional design style" then you have to pass a seed to every function that requires "random" numbers. There is no other choice. All other options require a side-effect or lose referential transparency.
So if you use random numbers in multiple functions, you have to provide a different seed to each of those functions.
The seed is just a compact way of representing an infinite sequence of random numbers, so an alternative is to generate the random numbers outside the function and past the values to the function rather than the seed.
Upvotes: 2
Reputation: 20960
You use a different type of PRNG, that supports splitting. Then you split the generator on the main actor as many times as you need, and send one subgenerator to each of the other actors.
As an example, JAX does this by using a generator called threefry.
Upvotes: 2
Reputation: 51693
You can introduce a separate actor that will reply with next random number.
For example actors will send GetNextRandomNumber
to this actor and it will reply NextRandomNumber(number)
.
This actor will manage its state (seed
) itself.
Actually sending a message to this actor will be a side effect.
Normally generating a random number is a side effect because function that is free of side effects can't produce different outputs on the same input.
The idea of functional programming is not to avoid side effects but to control them. For example with IO
, State
etc.
Upvotes: 3