DEV Community

Cover image for How To Become a Millionaire Scala Developer
Stefan Compton
Stefan Compton

Posted on

How To Become a Millionaire Scala Developer

Or, Simple Web API with Http4s and Cats Effect

"The EuroMillions lottery gives you a 1 in 140 million chance of not going to work tomorrow. With alcohol it's 1 in 5." - Anonymous

OK, I'm unlikely to get any "thank you" messages from Millionaires, but if you follow this post then there is a sense in which you will become a millionaire. If our physicists are right. And for a given meaning of become.

Getting All Quantum

So, the set up is this. We use a quantum random number generator, which then splits the universe in 140 million or so worlds, and...

Wait, Splits the Universe?

According to the most austere and plain reading of quantum mechanics, any quantum event which we perceive as probabilistic, is not really - all eventualities actually happen, none of them is a special real eventuality, and it's just our parochial view on the world that only permits us to see one outcome.

So given all that, we use a quantum random number generator, create all those universes and guarantee that a version of me (or you) will win the lottery.

Enough Theory, Let's Get Coding

The first thing I did was create a service for getting random numbers. The class RandomService takes a function that for a given number will generate that many random numbers. I'm using the Cats Effect IO monad to wrap the calculation, as I don't want to immediately evaluate it. Since the call will be to a front end API, and it in turn wants to call the back-end API, I don't want to run any of it explicitly synchronously.

class RandomService(randomFunction: Int => IO[Seq[Int]])
Enter fullscreen mode Exit fullscreen mode

I'm generating randoms in the service, then using them to perform a Fisher-Yates shuffle. If you ask for 5 numbers from 1-50 then it will generate all numbers from 1-50 and take 5 of them.

def generateNumberSequence(numberGroup: NumberGroup): IO[Seq[Int]] = {
    randomFunction(numberGroup.size).map(rands => {
        val result = shuffle(1 to numberGroup.size, rands)
            .take(numberGroup.count)
        if (numberGroup.isFullList) result else result.sorted
    })
}

@tailrec
final def shuffle(values: Seq[Int], randoms: Seq[Int], index: Int = 0): Seq[Int] = {
    val n = values.size
    if (index == n - 1) values
    else shuffle(swap(values, index, index + (randoms(index) % (n - index))), randoms, index + 1)
}

@tailrec
final def swap(seq: Seq[Int], i: Int, j: Int): Seq[Int] = {
    if (i == j) seq
    else if (j < i) swap(seq, j, i)
    else seq.take(i) ++ Seq(seq(j)) ++ seq.slice(i + 1, j) ++ Seq(seq(i)) ++ seq.drop(j + 1)
}
Enter fullscreen mode Exit fullscreen mode

the NumberGroup is just a data type to carry the magnitude of numbers requested and how many of them. Note that we sort the numbers you request except when the group is full - if you request all numbers from 1-50 then the order is important, you don't just want 1,2,3 ... 50!

The other interesting part is the web service Lotto

trait Lotto {
  def generate(vals: Seq[NumberGroup]): IO[Lotto.Response]
}

object Lotto {
  final case class Response(nums: Seq[(Int, Seq[Int])])
  val randomService = new RandomService(QRNG.f)
  object Response {
    implicit val lottoEncoder: Encoder[Response] = (resp: Response) =>
      Json.obj(
        ("Your Numbers:",
      Json.fromFields(resp.nums.map(t => (t._1.toString, t._2.asJson)))
        )
      )
  }

  def impl: Lotto = (vals: Seq[NumberGroup]) => {
    val resp = vals.map(randomService.generateNumberSequence).sequence
    resp.map(r => Response(r.zipWithIndex.map(t => (t._2, t._1))))
  }
Enter fullscreen mode Exit fullscreen mode

I love the straightforward declarative nature of the code. Also how concise it is. Most of the complexity is in getting the response to look OK. It still needs some work though!

Notice also the .sequence method that takes a sequence of our IO monads and sequences them into one IO. Conceptually it's reorganizing the result without having to perform the computation. Nice!

All that's left is to run our Http4s server, which couldn't be simpler.

  def run: IO[Nothing] = {
    val lottoAlg = Lotto.impl
    val httpApp = QuantumMillionaireRoutes.lottoRoutes(lottoAlg).orNotFound
    val finalHttpApp = Logger.httpApp(logHeaders = true, logBody = true)(httpApp)
    EmberServerBuilder.default[IO]
        .withHost(ipv4"0.0.0.0")
        .withPort(port"8080")
        .withHttpApp(finalHttpApp)
        .build
  }.useForever
Enter fullscreen mode Exit fullscreen mode

And there it is. All the code is on my GitHub here

And when you do become a Millionaire, please let that version of me know - if I know him, he'll put it on his CV ...

Latest comments (1)

Collapse
 
christhomas412 profile image
christhomas412

Thank you for sharing this fascinating insight into quantum mechanics and the intriguing concept of becoming a millionaire through quantum random number generation. Your concise and well-explained code snippets make it easier to grasp this complex Winning idea. Coding for such a unique and imaginative project must have been quite an experience!