I'm trying to understand how to leverage monads in scala to solve simple problems as way of building up my familiarity. One simple problem is estimating PI using a functional random number generator. I'm including the code below for a simple stream based approach.
I'm looking for help in translating this to a monadic approach. For example, is there an idiomatic way convert this code to using the state (and other monads) in a stack safe way?
trait RNG {
def nextInt: (Int, RNG)
def nextDouble: (Double, RNG)
}
case class Point(x: Double, y: Double) {
val isInCircle = (x * x + y * y) < 1.0
}
object RNG {
def nonNegativeInt(rng: RNG): (Int, RNG) = {
val (ni, rng2) = rng.nextInt
if (ni > 0) (ni, rng2)
else if (ni == Int.MinValue) (0, rng2)
else (ni + Int.MaxValue, rng2)
}
def double(rng: RNG): (Double, RNG) = {
val (ni, rng2) = nonNegativeInt(rng)
(ni.toDouble / Int.MaxValue, rng2)
}
case class Simple(seed: Long) extends RNG {
def nextInt: (Int, RNG) = {
val newSeed = (seed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFFL
val nextRNG = Simple(newSeed)
val n = (newSeed >>> 16).toInt
(n, nextRNG)
}
def nextDouble: (Double, RNG) = {
val (n, nextRNG) = nextInt
double(nextRNG)
}
}
}
object PI {
import RNG._
def doubleStream(rng: Simple):Stream[Double] = rng.nextDouble match {
case (d:Double, next:Simple) => d #:: doubleStream(next)
}
def estimate(rng: Simple, iter: Int): Double = {
val doubles = doubleStream(rng).take(iter)
val inside = (doubles zip doubles.drop(3))
.map { case (a, b) => Point(a, b) }
.filter(p => p.isInCircle)
.size * 1.0
(inside / iter) * 4.0
}
}
// > PI.estimate(RNG.Simple(10), 100000)
// res1: Double = 3.14944
I suspect I'm looking for something like replicateM from the Applicative monad in cats but I'm not sure how to line up the types or how to do it in a way that doesn't accumulate intermediate results in memory. Or, is there a way to do it with a for comprehension that can iteratively build up Points?
Id you want to iterate using monad in a stack safe way, then there is a tailRecM method implemented in Monad type class:
// assuming random generated [-1.0,1.0]
def calculatePi[F[_]](iterations: Int)
(random: => F[Double])
(implicit F: Monad[F]): F[Double] = {
case class Iterations(total: Int, inCircle: Int)
def step(data: Iterations): F[Either[Iterations, Double]] = for {
x <- random
y <- random
isInCircle = (x * x + y * y) < 1.0
newTotal = data.total + 1
newInCircle = data.inCircle + (if (isInCircle) 1 else 0)
} yield {
if (newTotal >= iterations) Right(newInCircle.toDouble / newTotal.toDouble * 4.0)
else Left(Iterations(newTotal, newInCircle))
}
// iterates until Right value is returned
F.tailRecM(Iterations(0, 0))(step)
}
calculatePi(10000)(Future { Random.nextDouble }).onComplete(println)
It uses by-name param because you could try to pass there something like Future (even though the Future is not lawful), which are eager, so you would end up with evaluating the same thing time and time again. With by name param at least you have the chance of passing there a recipe for side-effecting random. Of course, if we use Option, List as a monad holding our "random" number, we should also expect funny results.
The correct solution would be using something that ensures that this F[A] is lazily evaluated, and any side effect inside is evaluated each time you need a value from inside. For that you basically have to use some of Effects type classes, like e.g. Sync from Cats Effects.
def calculatePi[F[_]](iterations: Int)
(random: F[Double])
(implicit F: Sync[F]): F[Double] = {
...
}
calculatePi(10000)(Coeval( Random.nextDouble )).value
calculatePi(10000)(Task( Random.nextDouble )).runAsync
Alternatively, if you don't care about purity that much, you could pass side effecting function or object instead of F[Int] for generating random numbers.
// simplified, hardcoded F=Coeval
def calculatePi(iterations: Int)
(random: () => Double): Double = {
case class Iterations(total: Int, inCircle: Int)
def step(data: Iterations) = Coeval {
val x = random()
val y = random()
val isInCircle = (x * x + y * y) < 1.0
val newTotal = data.total + 1
val newInCircle = data.inCircle + (if (isInCircle) 1 else 0)
if (newTotal >= iterations) Right(newInCircle.toDouble / newTotal.toDouble * 4.0)
else Left(Iterations(newTotal, newInCircle))
}
Monad[Coeval].tailRecM(Iterations(0, 0))(step).value
}
Here is another approach that my friend Charles Miller came up with. It's a bit more direct since it uses RNG directly but it follows the same approach provided by #Mateusz Kubuszok above that leverages Monad.
The key difference is that it leverages the State monad so we can thread the RNG state through the computation and generate the random numbers using the "pure" random number generator.
import cats._
import cats.data._
import cats.implicits._
object PICharles {
type RNG[A] = State[Long, A]
object RNG {
def nextLong: RNG[Long] =
State.modify[Long](
seed ⇒ (seed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFFL
) >> State.get
def nextInt: RNG[Int] = nextLong.map(l ⇒ (l >>> 16).toInt)
def nextNatural: RNG[Int] = nextInt.map { i ⇒
if (i > 0) i
else if (i == Int.MinValue) 0
else i + Int.MaxValue
}
def nextDouble: RNG[Double] = nextNatural.map(_.toDouble / Int.MaxValue)
def runRng[A](seed: Long)(rng: RNG[A]): A = rng.runA(seed).value
def unsafeRunRng[A]: RNG[A] ⇒ A = runRng(System.currentTimeMillis)
}
object PI {
case class Step(count: Int, inCircle: Int)
def calculatePi(iterations: Int): RNG[Double] = {
def step(s: Step): RNG[Either[Step, Double]] =
for {
x ← RNG.nextDouble
y ← RNG.nextDouble
isInCircle = (x * x + y * y) < 1.0
newInCircle = s.inCircle + (if (isInCircle) 1 else 0)
} yield {
if (s.count >= iterations)
Right(s.inCircle.toDouble / s.count.toDouble * 4.0)
else
Left(Step(s.count + 1, newInCircle))
}
Monad[RNG].tailRecM(Step(0, 0))(step(_))
}
def unsafeCalculatePi(iterations: Int) =
RNG.unsafeRunRng(calculatePi(iterations))
}
}
Thanks Charles & Mateusz for your help!
Related
I have implemented a Calculation class that takes 2 parameters: a calculation input that is a call-by-name param and also a cost. When I try to flatMap calculations, the first part of it gets executed. Is it possible to defer everything in flatMap and still provide the total cost?
class Calculation[+R](input: => R, val cost: Int = 0) {
def value: R = input
def map[A](f: R => A): Calculation[A] =
new Calculation(f(input), cost)
def flatMap[A](f: R => Calculation[A]): Calculation[A] = {
val step = f(input)
new Calculation(step.value, cost + step.cost)
}
}
object Rextester extends App {
val f1 = new Calculation({
println("F1")
"F1"
})
val f2 = f1.flatMap(s => new Calculation({
println("F2")
s + " => F2"
}))
println(f2.cost)
}
Once f2 is declared (flatMap is called), we can see that "F1" will be printed. The printed cost is "15", which is correct, but I would like to have the actual calculation fully deferred, meaning that I shouldn't see the f1 being executed when I calculate the cost.
You just need a little more laziness, so that the cost isn't eagerly evaluated in the flatMap:
class Calculation[+R](input: => R, c: => Int = 0) {
def value: R = input
lazy val cost: Int = c
def map[A](f: R => A): Calculation[A] =
new Calculation(f(input), cost)
def flatMap[A](f: R => Calculation[A]): Calculation[A] = {
lazy val step = f(value)
new Calculation(step.value, cost + step.cost)
}
}
Note that this still might not have exactly the semantics you want (e.g. calling f2.value twice in a row will results in both F1 and F2 being printed the first time, and only F2 the second), but it does keep the side effect from occurring when f2 is defined.
If I understand your requirement
defer everything in flatMap and still provide the total cost
correctly, then you want to compute an estimate for the total costs before making any computations. I don't see how this is supposed to work with the signature flatMap[A](f: R => Calculation[A]): Calculation[A] - your cost is attached to Calculation[A], and your Calculation[A] depends on a concrete instance of R, so you cannot compute the cost before computing R.
Constant costs for computation steps
Here is a completely different proposal:
sealed trait Step[-A, +B] extends (A => B) { outer =>
def estimatedCosts: Int
def andThen[U >: B, C](next: Step[U, C]): Step[A, C] = new Step[A, C] {
def apply(a: A): C = next(outer(a))
def estimatedCosts = outer.estimatedCosts + next.estimatedCosts
}
def result(implicit u_is_a: Unit <:< A): B = this(u_is_a(()))
}
type Computation[+R] = Step[Unit, R]
The trait Step represents a computation step, for which the costs do not depend on the input. It's essentially just a Function[A, B] with an integer value attached to it. Your Computation[R] then becomes a special case, namely Step[Unit, R].
Here is how you can use it:
val x = new Step[Unit, Int] {
def apply(_u: Unit) = 42
def estimatedCosts = 0
}
val mul = new Step[Int, Int] {
def apply(i: Int) = {
println("<computing> adding is easy")
i + 58
}
def estimatedCosts = 10
}
val sqrt = new Step[Int, Double] {
def apply(i: Int) = {
println("<computing> finding square roots is difficult")
math.sqrt(i)
}
def estimatedCosts = 50
}
val c: Computation[Double] = x andThen mul andThen sqrt
println("Estimated costs: " + c.estimatedCosts)
println("(nothing computed so far)")
println(c.result)
If you run it, you obtain:
Estimated costs: 60
(nothing computed so far)
<computing> adding is easy
<computing> finding square roots is difficult
10.0
What it does is the following:
It starts with value 42, adds 58 to it, and then computes the square root of the sum
Addition is set to cost 10 units, square root costs 50.
It gives you the cost estimate of 60 units, without performing any computations.
Only when you invoke .result does it compute the actual result 10.0
Admittedly, it's not very useful for anything except very coarse order-of-magnitude estimates. It's so coarse that even using Ints barely makes any sense.
Non-constant costs per step
You can make your cost estimates more accurate by keeping track of a size estimate as follows:
trait Step[-A, +B] extends (A => B) {
def outputSizeEstimate(inputSizeEstimate: Int): Int
def costs(inputSizeEstimate: Int): Int
}
trait Computation[+R] { outer =>
def result: R
def resultSize: Int
def estimatedCosts: Int
def map[S](step: Step[R, S]): Computation[S] = new Computation[S] {
def result: S = step(outer.result)
def estimatedCosts: Int = outer.estimatedCosts + step.costs(outer.resultSize)
def resultSize: Int = step.outputSizeEstimate(outer.resultSize)
}
}
val x = new Computation[List[Int]] {
def result = (0 to 10).toList
def resultSize = 10
def estimatedCosts = 10
}
val incrementEach = new Step[List[Int], List[Int]] {
def outputSizeEstimate(inputSize: Int) = inputSize
def apply(xs: List[Int]) = {
println("incrementing...")
xs.map(1.+)
}
def costs(inputSize: Int) = 3 * inputSize
}
val timesSelf = new Step[List[Int], List[(Int, Int)]] {
def outputSizeEstimate(n: Int) = n * n
def apply(xs: List[Int]) = {
println("^2...")
for (x <- xs; y <- xs) yield (x, y)
}
def costs(n: Int) = 5 * n * n
}
val addPairs = new Step[List[(Int, Int)], List[Int]] {
def outputSizeEstimate(n: Int) = n
def apply(xs: List[(Int, Int)]) = {
println("adding...")
xs.map{ case (a, b) => a + b }
}
def costs(n: Int) = 7 * n
}
val y = x map incrementEach map timesSelf map addPairs
println("Estimated costs (manually): " + (10 + 30 + 500 + 700))
println("Estimated costs (automatically): " + y.estimatedCosts)
println("(nothing computed so far)")
println(y.result)
The output looks encouraging:
Estimated costs (manually): 1240
Estimated costs (automatically): 1240
(nothing computed so far)
incrementing...
^2...
adding...
List(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...[omitted]..., 20, 21, 22)
Note that the approach is not restricted to lists and integers: the size estimates can be arbitrarily complicated. For example, they could be dimensions of matrices or tensors. Actually, they don't have to be sizes at all. Those estimates could just as well contain any other kind of "static conservative estimates", like types or logical predicates.
Non-constant costs, using Writer
Using the Writer monad from Cats, we can express the same idea more succinctly by replacing the two methods outputSizeEstimate and costs on a Step by a single method that takes an Int and returns a Writer[Int, Int]:
Writers .value corresponds to size estimate for the output
Writers .written corresponds to costs of the step (which might depend on the input size)
Full code:
import cats.data.Writer
import cats.syntax.writer._
import cats.instances.int._
object EstimatingCosts extends App {
type Costs = Int
type Size = Int
trait Step[-A, +B] extends (A => B) {
def sizeWithCosts(inputSizeEstimate: Size): Writer[Costs, Size]
}
object Step {
def apply[A, B]
(sizeCosts: Size => (Size, Costs))
(mapResult: A => B)
: Step[A, B] = new Step[A, B] {
def apply(a: A) = mapResult(a)
def sizeWithCosts(s: Size) = { val (s2, c) = sizeCosts(s); Writer(c, s2) }
}
}
trait Computation[+R] { outer =>
def result: R
def sizeWithCosts: Writer[Costs, Size]
def size: Size = sizeWithCosts.value
def costs: Costs = sizeWithCosts.written
def map[S](step: Step[R, S]): Computation[S] = new Computation[S] {
lazy val result: S = step(outer.result)
lazy val sizeWithCosts = outer.sizeWithCosts.flatMap(step.sizeWithCosts)
}
}
object Computation {
def apply[A](initialSize: Size, initialCosts: Costs)(a: => A) = {
new Computation[A] {
lazy val result = a
lazy val sizeWithCosts = Writer(initialCosts, initialSize)
}
}
}
val x = Computation(10, 10){ (0 to 10).toList }
val incrementEach = Step(n => (n, 3 * n)){ (xs: List[Int]) =>
println("incrementing...")
xs.map(1.+)
}
val timesSelf = Step(n => (n * n, 5 * n * n)) { (xs: List[Int]) =>
println("^2...")
for (x <- xs; y <- xs) yield (x, y)
}
val addPairs = Step(n => (n, 7 * n)) { (xs: List[(Int, Int)]) =>
println("adding...")
xs.map{ case (a, b) => a + b }
}
val y = x map incrementEach map timesSelf map addPairs
println("Estimated costs (manually): " + (10 + 30 + 500 + 700))
println("Estimated costs (automatically): " + y.costs)
println("(nothing computed so far)")
println(y.result)
}
The output stays exactly the same as in the previous section.
PS: I think I came up with a more concise way to summarize this entire answer:
Use the product category of the ordinary ambient Scala category (types and functions) with the monoid of endomorphisms on object Int in the Kleisli category of Writer[Int, ?].
In some hypothetical language, the answer might have been:
Use Sc * End{Kl(Writer[Int, ?])}[Int].
First of all there is no reason to re-invent your own Functor and FlatMap and I would strongly advice you to use existing implementation.
If you need deffered computation than cats.Writer[Int, ?] is your friend.
With its support you can write your cost as well as get functor and monad instances.
Let me give you an example. We start with some initial cost
val w = Writer.put("F1")(0)
w.flatMap(v => Writer.value(v + "F2"))
The following logic identifies the combination of integers summing to n that produces the maximum product:
def bestProd(n: Int) = {
type AType = (Vector[Int], Long)
import annotation._
// #tailrec (umm .. nope ..)
def bestProd0(n: Int, accum : AType): AType = {
if (n<=1) accum
else {
var cmax = accum
for (k <- 2 to n) {
val tmpacc = bestProd0(n-k, (accum._1 :+ k, accum._2 * k))
if (tmpacc._2 > cmax._2) {
cmax = tmpacc
}
}
cmax
}
}
bestProd0(n, (Vector(), 1))
}
This code does work:
scala> bestProd(11)
res22: (Vector[Int], Long) = (Vector(2, 3, 3, 3),54)
Now it was not a surprise to me that #tailrec did not work. After all the recursive invocation is not in the tail position. Is is possible to reformulate the for loop to instead do a proper single-call to achieve the tail recursion?
I don't think it's possible if you're trying to stick close to the stated algorithm. Rethinking the approach you could do something like this.
import scala.annotation.tailrec
def bestProd1(n: Int) = {
#tailrec
def nums(acc: Vector[Int]): Vector[Int] = {
if (acc.head > 4)
nums( (acc.head - 3) +: 3 +: acc.tail )
else
acc
}
val result = nums( Vector(n) )
(result, result.product)
}
It comes up with the same results (as far as I can tell) except for I don't split 4 into 2,2.
Background
I started out with a Shuffler class that does two things:
Shuffles n:Int indexes
Puts them into n_tranches:Int
I am trying to refactor this code such that almost the entire implementation is in Trancheur, which puts the indexes into n_tranches.
For example, I may want to put 50 cards into 6 stacks, which I call tranches.
Original Code
class Shuffler( n:Int, n_tranches:Int )
{
val v = scala.util.Random.shuffle( (0 to n-1).toVector )
// returns tranche[0,n_tranches-1] which we live in
def tranche( i:Int ) = idxs(i).map( v ).sorted.toVector
private val idxs = cut( 0 to (n-1), n_tranches ).toVector
private def cut[A](xs: Seq[A], n: Int) = {
val (quot, rem) = (xs.size / n, xs.size % n)
val (smaller, bigger) = xs.splitAt(xs.size - rem * (quot + 1))
smaller.grouped(quot) ++ bigger.grouped(quot + 1)
}
}
New Code
class Shuffler( n:Int, n_tranches:Int )
extends Trancheur( n, n_tranches, scala.util.Random.shuffle )
{
}
class Trancheur( n:Int, n_tranches:Int, shuffler ) // WHAT SHOULD I PUT HERE?!?!?!?
{
val v = shuffler( (0 to n-1).toVector )
// returns tranche[0,n_tranches-1] which we live in
def tranche( i:Int ) = idxs(i).map( v ).sorted.toVector
private val idxs = cut( 0 to (n-1), n_tranches ).toVector
private def cut[A](xs: Seq[A], n: Int) = {
val (quot, rem) = (xs.size / n, xs.size % n)
val (smaller, bigger) = xs.splitAt(xs.size - rem * (quot + 1))
smaller.grouped(quot) ++ bigger.grouped(quot + 1)
}
}
Problem
I want Shuffler to call Trancheur with the functor scala.util.Random.shuffle. I think the call is fine.
But as a default, I want the Trancheur to have an identity functor which does nothing: it just returns the same results as before. I am having trouble with the constructor signature and with what to define as the identity functor.
NOTE: I apologize in advance if I have used the wrong term in calling scala.util.Random.shuffle a functor - that's what we call it in C++. Not sure if Functor means something else in Scala.
shuffle is a function. So shuffler (the parameter) should expect a function. For your case Seq[Int] => Seq[Int] should be sufficient. Scala also provides a predefined identity function.
This should do it:
class Trancheur( n:Int, n_tranches:Int, shuffler: Seq[Int] => Seq[Int] = identity)
I'd love to have let construct similar to the one in Haskell in Scala. I tried a few ways, but none seems to be good. Here's some code:
object CustomLet extends App {
val data = for (i <- 1 to 1024; j <- 1 to 512) yield (i % j) * i * (i + 1) - 1
def heavyCalc() = { println("heavyCalc called"); data.sum }
def doSomethingWithRes(res: Int) = {
println(s"${res * res}")
1
}
def cond(value: Int): Boolean = value > 256
// not really usable, even though it's an expression (2x heavyCalc calls)
def withoutLet() = if (cond(heavyCalc())) doSomethingWithRes(heavyCalc()) else 0
// not an expression
def letWithVal(): Int = {
val res = heavyCalc()
if (cond(res)) doSomethingWithRes(res)
else 0
}
// a lot of code to simulate "let", at least it is an expression
def letWithMatch(): Int = heavyCalc() match {
case res => if (cond(res)) doSomethingWithRes(res) else 0
}
// not perfect solution from
// http://stackoverflow.com/questions/3241101/with-statement-equivalent-for-scala/3241249#3241249
def let[A, B](param: A)(body: A => B): B = body(param)
// not bad, but I'm not sure if it could handle more bindings at once
def letWithApp(): Int = let(heavyCalc()) {res => if (cond(res)) doSomethingWithRes(res) else 0}
List[(String, () => Int)](
("withoutLet", withoutLet),
("letWithVal", letWithVal),
("letWithMatch", letWithMatch),
("letWithApp", letWithApp)
).foreach(
item => item match {
case (title, func) => {
println(s"executing $title")
val ret = func()
println(s"$title finished with $ret")
println()
}
}
)
}
This is the ideal look of it (with only one binding, more could be separated by ,; not sure about the in keyword):
// desired look
def letTest(): Int =
let res = heavyCalc() in
if (cond(res)) doSomethingWithRes(res) else 0
I'm not sure if it's possible, but I have no experience with most of advanced Scala stuff like macros, so I can't really tell.
EDIT1: To be clear, the main things I'm expecting from it are: being expression and relatively simple syntax (like the one outlined above).
You could use a forward pipe:
object ForwardPipeContainer {
implicit class ForwardPipe[A](val value: A) extends AnyVal {
def |>[B](f: A => B): B = f(value)
}
}
to be used like this:
import ForwardPipeContainer._
def f(i: Int) = i * i
println( f(3) |> (x => x * x) )
You can put multiple arguments in a tuple:
println( (f(2), f(3)) |> (x => x._1 * x._2) )
which looks better if combined with partial function synatx:
println( (f(2), f(3)) |> { case (x, y) => x * y } )
This answer is a variation of What is a good way of reusing function result in Scala, and both are based on Cache an intermediate variable in an one-liner where I got the initial idea from.
def letTest(): Int =
let res = heavyCalc() in
if (cond(res)) doSomethingWithRes(res) else 0
I would write this:
def letTest(): Int = {
val res = heavyCalc()
if (cond(res)) doSomethingWithRes(res) else 0
}
Ignoring laziness, let is just a construct that introduces a lexical scope, binds some terms to some names then returns an expression. So in Scala you would do
{ // new lexical scope
// bind terms section
val a = f()
def b = a + g() // may be I don't want g to be evaluated unless b is needed
val c = h()
// result expression
if (c) b else a
}
Macros should be able to enforce this syntactic layout if you want to ensure that there is nothing else going on in the block. There is actually a SIP (Scala Improvement Process) proposal called Spores that would enforce some of the same constraints (and an additional one: that you don't capture a reference of an enclosing object unknowingly).
Note that blocks in Scala are expressions that evaluate to the last expression in the block. So let me take a random let example from Haskell:
aaa = let y = 1+2
z = 4+6
in let f = 3
e = 3
in e+f
This translates to:
val aaa = {
val y = 1 + 2
val z = 4 + 6
val u = {
val f = 3
val e = 3
e + f
}
u
}
As you can see the block statement can be used as an expression.
Context
object Fibonacci {
final val Threshold = 30
def fibonacci(n: Int)(implicit implementation: Fibonacci): Int = implementation match {
case f: functional.type if n > Threshold => fibonacci(n)(imperativeWithLoop)
case f: imperativeWithRecursion.type => f(n)
case f: imperativeWithLoop.type => f(n)
case f: functional.type => f(n)
}
sealed abstract class Fibonacci extends (Int => Int)
object functional extends Fibonacci {
def apply(n: Int): Int =
if (n <= 1) n else apply(n - 1) + apply(n - 2)
}
object imperativeWithRecursion extends Fibonacci {
def apply(n: Int) = {
#scala.annotation.tailrec
def recursion(i: Int, f1: Int, f2: Int): Int =
if (i == n) f2 else recursion(i + 1, f2, f1 + f2)
if (n <= 1) n else recursion(1, 0, 1)
}
}
implicit object imperativeWithLoop extends Fibonacci {
def apply(n: Int) = {
def loop = {
var res = 0
var f1 = 0
var f2 = 1
for (i <- 2 to n) {
res = f1 + f2
f1 = f2
f2 = res
}
res
}
if (n <= 1) n else loop
}
}
}
Example
object Main extends App { // or REPL
import Fibonacci._
println(fibonacci(6)(imperativeWithRecursion)) // 8
println(fibonacci(6)(imperativeWithLoop)) // 8
println(fibonacci(6)(functional)) // 8
println(fibonacci(6)) // 8
println(fibonacci(40)(functional)) // 102334155
}
Explanation
I was playing with Scala and ended up with this code. It compiles and runs, but...
Questions:
1) Is there any difference (readbility, performance, known bugs, anything) between
case f: functional.type => f(n)
and
case `functional` => functional(n)
This is supposed to be more of a discussion, so I'm not only interested in facts. Any opinion is welcomed.
2) Look at the first line of the fibonacci method. Here it is:
case f: functional.type if n > Threshold => fibonacci(n)(imperativeWithLoop)
If I leave the 2nd parameter list (imperativeWithLoop) out, the code compiles but enters an infinite loop when I run it. Does anyone know why? The default implementation imperativeWithLoop is known to the compiler (no errors are produced). So why doesn't it get implicitly invoked? (I assume it doesn't)
Regarding the first question, there are small differences, but none that matter here. But it would be better if you uppercased the objects, in which case you could write this:
case Functional => Functional(n)
Regarding the second question, if you leave out imperativeWithLoop, it will select the implicit Fibonacci closest in scope -- implementation (which has already be found to be equal to funcional). So it will call itself with the exact same parameters as it had called before, and, therefore, enter an infinite loop.