Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems with Scala Numeric[T]

Tags:

types

scala

I have a numeric solving function (Double => Double) where I tried to be clever and use the Numeric[T] for keeping the two kinds of numbers apart.

This did not turn out to be easy. The remaining problems are:

  • how to do division; Numeric[T] only has plus, minus etc. operators.
  • why doesn't the compiler find the implicit evidence$1: Numeric[Double] functions (see compiler output below)

Ideally, I'd like to say, "A and B are both Double, but tell me if I mix them with each other".

Here's the code:

import scala.annotation.tailrec

class Sweep[A: Numeric, B: Numeric]( fDiff: A => B, initialSeed: A, initialStep: A, bEps: B )
{
  val anum= evidence$1
  val bnum= evidence$2

  assert( anum.signum(initialStep) > 0 )
  assert( bnum.lt( fDiff(initialSeed), fDiff( anum.plus(initialSeed,initialStep) )) )   // check that it's an increasing function

  @tailrec
  private def sweep( seed: A, step: A ): A = {
    val bDiff= fDiff(seed)

    if ( bnum.lt( bnum.abs(bDiff), bEps) ) {  // done
      seed
    } else if ( bnum.signum(bDiff) != anum.signum(step) ) {
      sweep( anum.plus(seed,step), step )   // continue, same step and direction ('bDiff' should go smaller)
    } else {
      val newStep = anum.toDouble(step) / -2.0
      sweep( anum.minus(seed,newStep), newStep )    // reverse, smaller step
    }
  }

  // Make sure we take the initial step in the right direction
  //  
  private lazy val stepSign= -bnum.signum( fDiff(initialSeed) )

  def apply: A = sweep( initialSeed, stepSign * initialStep )
}

object TestX extends App {

  val t= new Sweep( (a: Double) => (a*a)-2, 1.0, 0.5, 1e-3 )()

  println( t, math.sqrt(2.0) )
}

I've tried it also with the older (implicit anum: Numeric[A]) parameters, but was unable to have two such (both for A and B).

Here's what the compiler says (Scala 2.9):

fsc -deprecation -d out-make -unchecked src/xxx.scala
src/xxx.scala:25: error: type mismatch;
 found   : newStep.type (with underlying type Double)
 required: A
      sweep( anum.minus(seed,newStep), newStep )    // reverse, smaller step
                             ^
src/xxx.scala:33: error: overloaded method value * with alternatives:
  (x: Double)Double <and>
  (x: Float)Float <and>
  (x: Long)Long <and>
  (x: Int)Int <and>
  (x: Char)Int <and>
  (x: Short)Int <and>
  (x: Byte)Int
 cannot be applied to (A)
  def apply: A = sweep( initialSeed, stepSign * initialStep )
                                              ^
src/xxx.scala:38: error: not enough arguments for constructor Sweep: (implicit evidence$1: Numeric[Double], implicit evidence$2: Numeric[Double])Sweep[Double,Double].
Unspecified value parameters evidence$1, evidence$2.
  val t= new Sweep( (a: Double) => (a*a)-2, 1.0, 0.5, 1e-3 )()
         ^
three errors found

Thanks for any ideas.

like image 564
akauppi Avatar asked Feb 15 '26 10:02

akauppi


1 Answers

You want to be working with Fractional instead of Numeric. The following compiles for me:

import scala.annotation.tailrec
import math.Fractional.Implicits._
import Ordering.Implicits._

class Sweep[A: Fractional, B: Fractional](fDiff: A => B, initialSeed: A, initialStep: A, bEps: B) {
  val aFractional = implicitly[Fractional[A]]

  assert(initialStep.signum > 0)
  assert(fDiff(initialSeed) < fDiff(initialSeed + initialStep))

  @tailrec
  private def sweep(seed: A, step: A): A = {
    val bDiff = fDiff(seed)
    if (bDiff.abs < bEps) {
      seed
    } else if (bDiff.signum != step.signum) {
      sweep(seed + step, step)
    } else {
      val one = aFractional.one
      val newStep = step / aFractional.fromInt(-2)
      sweep(seed - newStep, newStep)
    }
  }

  private lazy val stepSign = aFractional.fromInt(-fDiff(initialSeed).signum)
  def apply: A = sweep(initialSeed, stepSign * initialStep)
}

val sweep = new Sweep((a: Double) => (a*a)-2, 1.0, 0.5, 1e-3)
println(sweep.apply, math.sqrt(2.0))

Note that to get things like -2.0 in type A, you'll need to assemble them manually from Fractional.one or use Fractional.fromInt.

The other thing worth pointing out is the use of math.Fractional.Implicits and Ordering.Implicits which will allow you to use normal math syntax (+, <, /, etc.) instead of calling functions like plus and div.

like image 122
Steve Avatar answered Feb 17 '26 00:02

Steve



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!