Scala 3 "Module Abstractions" chapter in the book - scala

I am trying to use the 2nd approach ("Module Abstractions") from the "Opaque Types" chapter of the Scala3 book. The code is intended to show how one can make Scala use a logarithmic number representation.
The problem is that I do not see how the code from the book may be used. For me, it just does not work.
trait Logarithms:
type Logarithm
// operations on Logarithm
def add(x: Logarithm, y: Logarithm): Logarithm
def mul(x: Logarithm, y: Logarithm): Logarithm
// functions to convert between Double and Logarithm
def make(d: Double): Logarithm
def extract(x: Logarithm): Double
// extension methods to use `add` and `mul` as "methods" on Logarithm
extension (x: Logarithm)
def toDouble: Double = extract(x)
def + (y: Logarithm): Logarithm = add(x, y)
def * (y: Logarithm): Logarithm = mul(x, y)
// I added
def myToDouble: Double = extract(x)
object LogarithmsImpl extends Logarithms:
type Logarithm = Double
// operations on Logarithm
def add(x: Logarithm, y: Logarithm): Logarithm = make(x.toDouble + y.toDouble)
def mul(x: Logarithm, y: Logarithm): Logarithm = x + y
// functions to convert between Double and Logarithm
def make(d: Double): Logarithm = math.log(d)
def extract(x: Logarithm): Double = math.exp(x)
// I added
object Logarithms:
def apply(d: Double) = LogarithmsImpl.make(d)
The book says:
However, this abstraction is slightly leaky. We have to make sure to
only ever program against the abstract interface Logarithms and never
directly use LogarithmsImpl. Directly using LogarithmsImpl would make
the equality Logarithm = Double visible for the user, who might
accidentally use a Double where a logarithmic double is expected.
And now I try to use it:
$ ~/Downloads/scala3-3.1.3/bin/scala
Welcome to Scala 3.1.3 (14.0.1, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
scala> :load my/scala/logarithms1.scala
// defined trait Logarithms
// defined object LogarithmsImpl
// defined object Logarithms
scala> import Logarithms.*
scala> Logarithms(4)
val res0: LogarithmsImpl.Logarithm = 1.3862943611198906
scala> Logarithms(2)
val res1: LogarithmsImpl.Logarithm = 0.6931471805599453
scala> Logarithms(4).toDouble
val res2: Double = 1.3862943611198906
scala> Logarithms(4).myToDouble
-- [E008] Not Found Error: ----------------------------------------------------------------------------------------------------
1 |Logarithms(4).myToDouble
|^^^^^^^^^^^^^^^^^^^^^^^^
|value myToDouble is not a member of LogarithmsImpl.Logarithm, but could be made available as an extension method.
|
|The following import might fix the problem:
|
| import LogarithmsImpl.myToDouble
|
1 error found
In fact, importing LogarithmsImpl does not help much:
scala> import LogarithmsImpl.*
scala> import Logarithms.*
scala> val ll: Logarithm = make(4.0)
val ll: LogarithmsImpl.Logarithm = 1.3862943611198906
scala> val lll: Logarithm = make(2.0)
val lll: LogarithmsImpl.Logarithm = 0.6931471805599453
scala> lll+ll
val res0: Double = 2.0794415416798357
scala> extract(lll+ll)
val res1: Double = 7.999999999999998
The last line shows that the addition was the traditional floating-point addition rather than the override from the extension.
What am I missing?
UPD One more problem in the book's code: the line
def add(x: Logarithm, y: Logarithm): Logarithm = make(x.toDouble + y.toDouble)
should be
def add(x: Logarithm, y: Logarithm): Logarithm = make(x.myToDouble + y.myToDouble)
or, even better:
def add(x: Logarithm, y: Logarithm): Logarithm = x + math.log1p(math.exp(y-x))
UPD2 and it looks like the trait and the objects are not companions, a trait is not a class, only classes may be companions.

The most reasonable way of using this abstraction (that I can see) is like so:
#main
def main = {
val L = LogarithmsImpl
doStuffWithLogarithm(L)
}
def doStuffWithLogarithm(L: Logarithms): Double = {
import L.*
val a = L.make(1)
val b = L.make(2)
a.myToDouble
}
The gist of it is that you have to pass some instance of Logarithms and use it to access all methods so that the type alias equality doesn't leak.
scastie: https://scastie.scala-lang.org/KacperFKorban/4bnxghkMSzaPqn4vvy19QA/1
That's why it's not a great pattern. And why opaque types are useful.

Related

Generic method that take as input Breeze Vector or Breeze Matrix

I am trying to implement a method that can accept as input either a Breeze Vector or a Breeze Matrix. Something like
private def updateExponentialMovingAverage[T](tau: Double, initTheta: T, theta: T): T = {
tau * theta + (1 - tau) * initTheta
}
However this raises an issue with overloading and I cannot find an appropriate type to restrict T. Do you have any suggestions?
Breezes uses type classes for most of these kinds of things. The easiest thing to do is to use the VectorSpace type class, though unfortunately for DenseMatrices the VectorSpace typeclass is gated behind another import, which I regret.
import breeze.linalg._
import breeze.math._
import breeze.linalg.DenseMatrix.FrobeniusInnerProductDenseMatrixSpace.space
def updateExponentialMovingAverage[T](tau: Double, initTheta: T, theta: T)(implicit vs: VectorSpace[T, Double]): T = {
import vs._
theta * tau + initTheta * (1 - tau)
}
scala> val dv = DenseVector.rand(3)
val dv: breeze.linalg.DenseVector[Double] = DenseVector(0.21025028035007942, 0.6257503866073217, 0.8304592391242225)
scala> updateExponentialMovingAverage(0.8, dv, dv)
val res0: breeze.linalg.DenseVector[Double] = DenseVector(0.21025028035007942, 0.6257503866073217, 0.8304592391242225)
scala> val dm = DenseMatrix.rand(3, 3)
val dm: breeze.linalg.DenseMatrix[Double] = 0.6848513069340505 0.8995141354384266 0.3889904836678608
0.4554398871938874 0.03297082723969558 0.6708501327709948
0.4456539828672945 0.04289112062985678 0.9679002485942578
updateExponentialMovingAverage(0.8, dm, dm)
val res1: breeze.linalg.DenseMatrix[Double] = 0.6848513069340505 0.8995141354384266 0.3889904836678608
0.4554398871938874 0.03297082723969558 0.6708501327709948
0.4456539828672945 0.04289112062985678 0.9679002485942578

Why doesn't "1 Math.pow 2" work in scala?

In scala the following works
1 max 2
But the following don't
1 Math.pow 2
or
import Math.pow
1 pow 2
Can you explain why?
There are a couple of things going on here. In a nutshell:
Implicit casting of the constant "1" to an instance of Int
Leveraging of the "Space notation" for methods that take a single parameter
In the case of 1 max 2, the constant 1 is implicitly cast as an Int (i.e. an instance of class Int).
Since the Int class defines a method called "max" which takes a single parameter, you can use the space or infix notation. The following are all equivalent (run in spark-shell):
scala> 1 max 2
res8: Int = 2
scala> 1.max(2)
res9: Int = 2
scala> val x = 1 // Note the result type here is Int
x: Int = 1
scala> x.max(2)
res10: Int = 2
scala> x max (2)
res11: Int = 2
scala> 1 // Note the result type here is *also* Int
res12: Int = 1
scala> 1. // Here are all of the methods that are defined for an Int
!= + << >> byteValue ensuring formatted isInfinity isValidByte isWhole notify signum toChar toInt toString until
## - <= >>> ceil eq getClass isInstanceOf isValidChar longValue notifyAll synchronized toDegrees toLong unary_+ wait
% -> == ^ compare equals hashCode isNaN isValidInt max round to toDouble toOctalString unary_- |
& / > abs compareTo floatValue intValue isNegInfinity isValidLong min self toBinaryString toFloat toRadians unary_~ →
* < >= asInstanceOf doubleValue floor isInfinite isPosInfinity isValidShort ne shortValue toByte toHexString toShort underlying
Note that there are a whole bunch of methods available on an Int for example max, min, +, - and so on. Looking at the signature of say, +, we can see that + is a method that takes a single parameter. Therefore we can do the following:
scala> 1 + 2 // No surprises here
res15: Int = 3
scala> 1.+(2) // Huh? This works because + is a method of Int that takes a single parameter.
// This is effectively the same as your max example.
res16: Int = 3
scala> 1.+ // Here are the signatures of the + method.
def +(x: Char): Int
def +(x: Long): Long
def +(x: Float): Float
def +(x: Short): Int
def +(x: Double): Double
def +(x: Byte): Int
def +(x: String): String
def +(x: Int): Int
scala> 1 + 'A' // From the above, we can see that the following is also valid
res17: Int = 66 // because the return type is Int
scala> 1 + "41"
res18: String = 141 // In this case, the + operator is a String concatenation operator Because the return type is String
scala> 1 + "x"
res19: String = 1x // Also because the return is String, but possible more intuitive.
To the pow part of the question.
Math.pow is a method that takes 2 parameters. Because it takes 2 parameters the space notation is not available to you.
Also, pow is not a method that is associated with an Int. It is pretty much like a static method of the Math class (actually Math is an object). So, just like you can't say x.pow(y,z) you can not say 1.pow(y, z) on the other hand you can say Math.pow(x, 2) - to get x squared - because that matches the signature of the pow method.
Here is the signature of Math.pow:
scala> Math.pow
def pow(x$1: Double,x$2: Double): Double
This is a little less exciting than +, but it is clear that it takes 2 Doubles and returns a Double. The example of say Math.pow(2,2) works even though integers are supplied as parameters (when Doubles are required) because the Ints are automatically cast to Double.
I hope this helps explain what you are seeing. I encourage you to try these examples in spark-shell, sbt or some other scala REPL.
Methods doesn't the same signature:
override def max(that: Long): Long
def pow(x$1: Double,x$2: Double): Double
The first is a member of Int* and can be called on 1 whereas the second is called on Math object and must have two parameters.
* well actually of RichInt by implicit conversion, but to an intuitive use perspective, it comes to the same thing so this subtlety shouldn't bother you
Method:
override def max(that: Int): Int = math.max(self, that)
is from scala.runtime.RichInt class bc evry Int is wrapped by:
#inline implicit def intWrapper(x: Int) = new runtime.RichInt(x)
of class scala.LowPriorityImplicits
But RichInt doesn't have any pow method. And you have to respec Math.pow signature:
public static double pow(double a, double b)
and call it with 2 arguments or use your own developed wrapper, like:
object MainClass {
implicit class IntHelper(i:Int) {
def pow(p:Int): Double = Math.pow(i, p)
}
def main(args: Array[String]): Unit = {
println(1 pow 2)
}
}
output:
1.0
1 max 2 is just syntactic sugar for 1.max(2).
As 1 is an integer literal, all methods defined for scala.Int can be applied in this way.
Unfortunately pow is not a method of scala.Int
But you can find the related method in scala.math.BigDecimal and scala.math.BigInt.
So the following would work:
BigInt(1) pow 2
The max function you have used is from Int.
It's signature is:
def max(that: Int): Int
Since 1 is an Int, you can call 1 max 2 with infix notation. it is simply 1.max(2).
The pow function is from Math.
It's signature is
def pow(x: Double, y: Double): Double
Since 1 is not a Math, you can't simply call 1 pow 2. On the other hand in Int, there is no method such that def pow(that: Int): Int.
The one you can use is pow(a,2) which is the Math implementation.
Scala.Math Documentation says that pow function takes two arguments of type Double and returns the value of the first argument raised to the power of the second argument.
This is the working version of the pow function:
import Math.pow
val a: Double = 1
pow(a,2) // returns 1.0

Is there a division operation that produces both quotient and reminder?

Currently I write some ugly code like
def div(dividend: Int, divisor: Int) = {
val q = dividend / divisor
val mod = dividend % divisor
(q, mod)
}
Is it specified in standard library?
A bit late to the game, but since Scala 2.8 this works:
import scala.math.Integral.Implicits._
val (quotient, remainder) = 5 /% 2
No (except for BigInt, as mentioned in other answers), but you can add it:
implicit class QuotRem[T: Integral](x: T) {
def /%(y: T) = (x / y, x % y)
}
will work for all integral types. You can improve performance by making separate classes for each type such as
implicit class QuotRemInt(x: Int) extends AnyVal {
def /%(y: Int) = (x / y, x % y)
}
In BigInt, note /% operation which delivers a pair with the division and the reminder (see API). Note for instance
scala> BigInt(3) /% BigInt(2)
(scala.math.BigInt, scala.math.BigInt) = (1,1)
scala> BigInt(3) /% 2
(scala.math.BigInt, scala.math.BigInt) = (1,1)
where the second example involves an implicit conversion from Int to BigInt.
BigInt does it
def /%(that: BigInt): (BigInt, BigInt)
Division and Remainder - returns tuple containing the result of divideToIntegralValue and the remainder.

when testing for convergence using successive approximation technique, why does this code divide by the guess twice?

While working through the coursera class on scala I ran into the code below (from another question asked here by Sudipta Deb.)
package src.com.sudipta.week2.coursera
import scala.math.abs
import scala.annotation.tailrec
object FixedPoint {
println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet
val tolerance = 0.0001 //> tolerance : Double = 1.0E-4
def isCloseEnough(x: Double, y: Double): Boolean = {
abs((x - y) / x) / x < tolerance
} //> isCloseEnough: (x: Double, y: Double)Boolean
def fixedPoint(f: Double => Double)(firstGuess: Double): Double = {
#tailrec
def iterate(guess: Double): Double = {
val next = f(guess)
if (isCloseEnough(guess, next)) next
else iterate(next)
}
iterate(firstGuess)
} //> fixedPoint: (f: Double => Double)(firstGuess: Double)Double
def myFixedPoint = fixedPoint(x => 1 + x / 2)(1)//> myFixedPoint: => Double
myFixedPoint //> res0: Double = 1.999755859375
def squareRoot(x: Double) = fixedPoint(y => (y + x / y) / 2)(1)
//> squareRoot: (x: Double)Double
squareRoot(2) //> res1: Double = 1.4142135623746899
def calculateAverate(f: Double => Double)(x: Double) = (x + f(x)) / 2
//> calculateAverate: (f: Double => Double)(x: Double)Double
def myNewSquareRoot(x: Double): Double = fixedPoint(calculateAverate(y => x / y))(1)
//> myNewSquareRoot: (x: Double)Double
myNewSquareRoot(2) //> res2: Double = 1.4142135623746899
}
My puzzlement concerns the isCloseEnough function.
I understand that for guesses which are large numbers, the difference between a guess and
the large value that the function returns could potentially be very big all the time, so we may never converge.
Conversely, if the guess is small, and if what f(x) produces is small then we will likely converge too quickly.
So dividing through by the guess like this:
def isCloseEnough(x: Double, y: Double): Boolean = {
abs((x - y) / x) / x < tolerance
}
makes perfect sense. (here is 'x' is the guess, and y is f_of_x.)
My question is why do why does the solution given divide by the guess TWICE ?
Wouldn't that undo all the benefits of dividing through by the guess the first time ?
As an example... let's say that my current guess and the value actually returned by the
function given my current x is as shown below:
import math.abs
var guess=.0000008f
var f_of_x=.00000079999f
And lets' say my tolerance is
var tolerance=.0001
These numbers look pretty close, and indeed, if i divide through by x ONCE, i see that the result
is less than my tolerance.
( abs(guess - f_of_x) / guess)
res3: Float = 1.2505552E-5
However, if i divide through by x TWICE the result is much greater than my tolerance, which would suggest
we need to keep iterating.. which seems wrong since guess and observed f(x) are so close.
scala> ( abs(guess - f_of_x) / guess) / guess
res11: Float = 15.632331
Thanks in advance for any help you can provide.
You are completely right, it does not make sense. Further, the second division is outside of the absolute value rendering the inequality true for any negative x.
Perhaps someone got confused with testing for quadratic convergence.

More on generic Scala functions

Trying to implement, in Scala, the following Haskell function (from Learn You a Haskell...) so that it works with Int, Double, etc.
doubleUs x y = x * 2 + y * 2
Note that this is similar to Scala: How to define "generic" function parameters?
Here's my attempt and error. Can someone explain what's happening and offer a solution. Thanks.
scala> def doubleUs[A](x:A,y:A)(implicit numeric: Numeric[A]): A = numeric.plus(numeric.times(x,2),numeric.times(y,2))
<console>:34: error: type mismatch;
found : Int(2)
required: A
def doubleUs[A](x:A,y:A)(implicit numeric: Numeric[A]): A = numeric.plus(numeric.times(x,2),numeric.times(y,2))
In addition to what #Dylan said, you can make it look a little less tedious by importing into scope the contents of Numeric implicit as shown below:
scala> def doubleUs[N](x: N, y: N)(implicit ev: Numeric[N]) = {
| import ev._
| x * fromInt(2) + y * fromInt(2)
| }
doubleUs: [N](x: N, y: N)(implicit ev: Numeric[N])N
scala> doubleUs(3, 4)
res9: Int = 14
scala> doubleUs(8.9, 1.2)
res10: Double = 20.2
You are using the Int literal 2 but scala is expecting the Numeric type A.
The Scala Numeric API has a utility function- def fromInt(x:Int): T. This is what you want to use, so replace your usage of 2 with numeric.fromInt(2)
def doubleUs[A](x:A,y:A)(implicit numeric: Numeric[A]): A =
numeric.plus (numeric.times (x, numeric.fromInt (2)), numeric.times (y, numeric.fromInt (2)))
Also, since a Numeric instance defines an implicit conversion to an Ops, you can import numeric._ and then say x * fromInt(2) + y * fromInt(2).
You need some implicits in scope:
def doubleUs[A](x: A, y: A)(implicit num: Numeric[A]) = {
import num._
implicit def fromInt(i: Int) = num.fromInt(i)
x * 2 + y * 2
}
Dylan essentially answered, but for what it's worth, let me suggest to use the context bound syntax instead of the implicit argument (both are equivalent, and the former is automatically rewritten into the latter by the compiler).
def doubleUs[A : Numeric](x : A, y : A) : A = {
val num = implicitly[Numeric[A]]
import num.{plus,times,fromInt}
plus(times(x, fromInt(2)), times(y, fromInt(2)))
}