Scala - simple design by contract - scala

I'm learning Scala as a personal project as I'm fed up with the verbosity of Java. I like a lot of what I see, but wonder if there's a way to efficiently implement some simple contracts on methods. I'm not (necessarily) after full DbC, but is there a way to: -
indicate that a parameter or a class field is REQUIRED, i.e. CANNOT be null. The Option thing seems to indicate cleanly if an OPTIONAL value is present, but I want to specify class invariants (x is required) and also to succinctly specify that a parameter is required. I know I can do "if's" throwing some kind of exception, but I want a language feature for this VERY common use-case. I like my interfaces tight, I dislike defensive programming.
Is it possible to define succinct and efficient (runtime performance) ranged types, such as "NonNegativeInt" - I want to say that a parameter is >= 0. Or within a range. PASCAL had these types and I found them excellent for communicating intent. That is one of the big drawbacks of C, C++, Java, etc. When I say succinct I mean I want to declare a variable of this type as easily as a normal int, not having to new each and every instance on the heap.

For point (1), Option should indeed be enough. This is because while scala supports null values, it does so mainly for compatibility with Java. Scala code should not contain null, values, and where it does it should be constrained to very localized places, and converted to an option as soon as possible (good scala code will never let null values propagate).
So in idiomatic scala, if a field or parameter is not of type Option this really means that it is required.
Now, there is also the (experimental and never fully supported as far as I can tell) NotNull trait. See How does the NotNull trait work in 2.8 and does anyone actually use it?
For point (2) scala 2.10 introduces value classes. With them, you could define your very own class that wraps Int without runtime overhead, and implement its operators as you see fit. The only places where you would have a runtime check would be when converting from a normal Int to your NonNegativeInt (throw an exception if the int is negative). Note that this check would be performed everytime you create a new NonNegativeInt, which also means everytime you perform an operation, so there would be a non-null runtime impact. But Pascal was in the very same situation (range checks are performed at runtime in Pascal) so I guess that you're OK with this.
UPDATE: Here is an example implementation of NonNegativeInt (here renamed to UInt):
object UInt {
def apply( i: Int ): UInt = {
require( i >= 0 )
new UInt( i )
}
}
class UInt private ( val i: Int ) extends AnyVal {
override def toString = i.toString
def +( other: UInt ) = UInt( i + other.i)
def -( other: UInt ) = UInt( i - other.i)
def *( other: UInt ) = UInt( i * other.i)
def /( other: UInt ) = UInt( i / other.i)
def <( other: UInt ) = i < other.i
// ... and so on
}
and some example usage in the REPL:
scala> UInt(123)
res40: UInt = 123
scala> UInt(123) * UInt(2)
res41: UInt = 246
scala> UInt(5) - UInt(8)
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:221)
at UInt$.apply(<console>:15)
...

What is this null of which you speak?
Seriously, bar null at the borders of your system, where it comes into contact with code you did not write. At that boundary you make sure all nullable values are converted to Option.
Likewise, don't use exceptions. As with null, bar them at the gate. Turn them into Either or use ScalaZ Validation.
As for dependent types (where the type interacts with or depends on specific values or subsets of values such as the natural numbers) it's more work. However, Spire has a Natural type. It might not be exactly what you want since it's arbitrary precision but it does impose the non-negative aspect of the natural numbers.
Addendum
Conversion from a nullable value to Option is trivially accommodated by the Scala Standard Library itself in the form of the Option factroy. To wit:
scala> val s1 = "Stringy goodness"
s1: String = Stringy goodness
scala> val s2: String = null
s2: String = null
scala> val os1 = Option(s1)
os1: Option[String] = Some(Stringy goodness)
scala> val os2 = Option(s2)
os2: Option[String] = None

The Scala standard library comes built-in with exactly these kinds of assertion mechanisms: the assert, assume, required, and ensuring methods. The latter two especially allow you to write preconditions and postconditions in a Design-By-Contract style. Simple example of natural number division:
def divide(x: Int, y: Int): Int = {
require(x > y, s"$x > $y")
require(y > 0, s"$y > 0")
x / y
} ensuring (_ * y == x)
The require calls throw an IllegalArgumentException if the requirements are not met, and show the interpolated string as the exception's message. The ensuring call throws an exception if the given condition doesn't hold.
More details at: https://madusudanan.com/blog/scala-tutorials-part-29-design-by-contract/
There's also a tool that does formal verification on a subset of Scala written in this style: https://github.com/epfl-lara/stainless

Related

correct setup for opaque type with underlying Numeric/Ordering instances

unclear to me if this is in fact the same question as here or here, apologies if this is a duplicate.
i would like to define a type Ordinate which is simply an Int under-the-hood:
package world
opaque type Ordinate = Int
given Ordering[Ordinate] with {
def compare(x: Ordinate, y: Ordinate): Int = x.compare(y)
}
i would like to be able to leverage the Numeric[Int] and Ordering[Int] methods so that it would be easy to define methods such as
package world
import Ordinate.given
class Boundary(dims: List[(Ordinate, Ordinate)]) {
def contains(o: Ordinate, dimension: Int): Boolean = {
val (min, max) = dims(dimension)
min <= o && o <= max
}
}
...forgetting for the meantime that this would blow up if dims was empty, dimension < 0 or dims.length <= dimension.
when i try and set this up, i get compiler errors at the call site:
value <= is not a member of world.Ordinate, but could be made available as an extension method.
One of the following imports might fix the problem:
import world.given_Ordering_Ordinate.mkOrderingOps
import math.Ordering.Implicits.infixOrderingOps
import math.Ordered.orderingToOrdered
more generally, it would be wicked cool if this were the case without any special given imports for files in the same package as Ordinate and even better, across the codebase. but that may be an anti-pattern that i've carried forward from my Scala 2 coding.
explicit given imports may be a better pattern but i'm still learning Scala 3 from Scala 2 here. i know if i created an implicit val o = Ordering.by(...) in the companion object of Ordinate in Scala 2, with Ordinate as a value class, i would get the effect i'm looking for (zero-cost type abstraction + numeric behaviors).
anyhow, i'm guessing i'm just missing a small detail here, thank you for reading and for any help.
Scala 3 has revised the rules for infix operators so that the author must (explicitly) expose infix operations such as x: T <= y: T for some custom type T.
I've found two ways to address this for an opaque type, both with drawbacks:
at the call site, have import math.Ordering.Implicits.infixOrderingOps in scope, which brings in a given instance that converts Ordering[T] into infix comparators. drawback: any file that wants these comparators needs the import line, adding more import boilerplate as the number of files using this opaque type increases.
package world
import Ordinate.given
import math.Ordering.Implicits.infixOrderingOps // <-- add this line
class Boundary(dims: List[(Ordinate, Ordinate)]) {
def contains(o: Ordinate, dimension: Int): Boolean = {
val (min, max) = dims(dimension)
min <= o && o <= max
}
}
add an infix extension method for each comparator you want to expose. drawback here is boilerplate of having to write out the very thing we're trying not to duplicate in each file.
type Ordinate = Int
object Ordinate {
extension (o: Ordinate) {
infix def <=(x: Ordinate): Boolean = o <= x // <-- add 'infix' here
}
}
i'm guessing for those more experienced with large programs, these drawbacks are better than the drawbacks associated with anything more than this least permission approach to givens. but this still doesn't seem to deliver on the promise of opaque types as a zero-cost abstraction for numeric types. what seems to be missing is something like "import a given and treat it's methods as infix for my type".

Why do you need Arbitraries in scalacheck?

I wonder why Arbitrary is needed because automated property testing requires property definition, like
val prop = forAll(v: T => check that property holds for v)
and value v generator. The user guide says that you can create custom generators for custom types (a generator for trees is exemplified). Yet, it does not explain why do you need arbitraries on top of that.
Here is a piece of manual
implicit lazy val arbBool: Arbitrary[Boolean] = Arbitrary(oneOf(true, false))
To get support for your own type T you need to define an implicit def
or val of type Arbitrary[T]. Use the factory method Arbitrary(...) to
create the Arbitrary instance. This method takes one parameter of type
Gen[T] and returns an instance of Arbitrary[T].
It clearly says that we need Arbitrary on top of Gen. Justification for arbitrary is not satisfactory, though
The arbitrary generator is the generator used by ScalaCheck when it
generates values for property parameters.
IMO, to use the generators, you need to import them rather than wrapping them into arbitraries! Otherwise, one can argue that we need to wrap arbitraries also into something else to make them usable (and so on ad infinitum wrapping the wrappers endlessly).
You can also explain how does arbitrary[Int] convert argument type into generator. It is very curious and I feel that these are related questions.
forAll { v: T => ... } is implemented with the help of Scala implicits. That means that the generator for the type T is found implicitly instead of being explicitly specified by the caller.
Scala implicits are convenient, but they can also be troublesome if you're not sure what implicit values or conversions currently are in scope. By using a specific type (Arbitrary) for doing implicit lookups, ScalaCheck tries to constrain the negative impacts of using implicits (this use also makes it similar to Haskell typeclasses that are familiar for some users).
So, you are entirely correct that Arbitrary is not really needed. The same effect could have been achieved through implicit Gen[T] values, arguably with a bit more implicit scoping confusion.
As an end-user, you should think of Arbitrary[T] as the default generator for the type T. You can (through scoping) define and use multiple Arbitrary[T] instances, but I wouldn't recommend it. Instead, just skip Arbitrary and specify your generators explicitly:
val myGen1: Gen[T] = ...
val mygen2: Gen[T] = ...
val prop1 = forAll(myGen1) { t => ... }
val prop2 = forAll(myGen2) { t => ... }
arbitrary[Int] works just like forAll { n: Int => ... }, it just looks up the implicit Arbitrary[Int] instance and uses its generator. The implementation is simple:
def arbitrary[T](implicit a: Arbitrary[T]): Gen[T] = a.arbitrary
The implementation of Arbitrary might also be helpful here:
sealed abstract class Arbitrary[T] {
val arbitrary: Gen[T]
}
ScalaCheck has been ported from the Haskell QuickCheck library. In Haskell type-classes only allow one instance for a given type, forcing you into this sort of separation.
In Scala though, there isn't such a constraint and it would be possible to simplify the library. My guess is that, ScalaCheck being (initially written as) a 1-1 mapping of QuickCheck, makes it easier for Haskellers to jump into Scala :)
Here is the Haskell definition of Arbitrary
class Arbitrary a where
-- | A generator for values of the given type.
arbitrary :: Gen a
And Gen
newtype Gen a
As you can see they have a very different semantic, Arbitrary being a type class, and Gen a wrapper with a bunch of combinators to build them.
I agree that the argument of "limiting the scope through semantic" is a bit vague and does not seem to be taken seriously when it comes to organizing the code: the Arbitrary class sometimes simply delegates to Gen instances as in
/** Arbirtrary instance of Calendar */
implicit lazy val arbCalendar: Arbitrary[java.util.Calendar] =
Arbitrary(Gen.calendar)
and sometimes defines its own generator
/** Arbitrary BigInt */
implicit lazy val arbBigInt: Arbitrary[BigInt] = {
val long: Gen[Long] =
Gen.choose(Long.MinValue, Long.MaxValue).map(x => if (x == 0) 1L else x)
val gen1: Gen[BigInt] = for { x <- long } yield BigInt(x)
/* ... */
Arbitrary(frequency((5, gen0), (5, gen1), (4, gen2), (3, gen3), (2, gen4)))
}
So in effect this leads to code duplication (each default Gen being mirrored by an Arbitrary) and some confusion (why isn't Arbitrary[BigInt] not wrapping a default Gen[BigInt]?).
My reading of that is that you might need to have multiple instances of Gen, so Arbitrary is used to "flag" the one that you want ScalaCheck to use?

Input validation with the scala type system

Having played a bit with Scala now, I question myself how you should do input validation in Scala.
This is what I have seen many times:
def doSomethingWithPositiveIntegers(i: Int) = {
require(i>0)
//do something
}
to bring matters to a head, it feels like doing this in Java:
void doSomething(Object o) {
if (!o instanceof Integer)
throw new IllegalArgumentException();
}
There, you first accept more than you are willing to accept, and then introduce some "guard" that only lets the "good ones" in. To be exact, you'd need these guards in every function that does something with positive integers, and in case you'd like for example to include zero later on, you'd need to change every function. Of course you can shift it to another function, but nevertheless you'd always need to rember to call the correct function, and it might not survive type refactorings etc. Does not sound that I'd like to have that. I was thinking about pushing this validation code to the data type itself, like this:
import scala.util.Try
object MyStuff {
implicit class PositiveInt(val value: Int) {
require(value>0)
}
implicit def positiveInt2Int(positiveInt: PositiveInt): Int = positiveInt.value
}
import MyStuff._
val i: MyStuff.PositiveInt = 5
val j: Int = i+5
println(i) //Main$$anon$1$MyStuff$PositiveInt#3a16cef5
println(j) //10
val sum = i + i
println(sum) //10
def addOne(i: MyStuff.PositiveInt) = i + 1
println(Try(addOne(-5))) //Failure(java.lang.IllegalArgumentException: requirement failed)
println(Try(addOne(5))) //Success(6)
Then I have a type PositiveInt that can only contain positive integers, and I can use it (almost) everywhere like an Int. Now, my API defines what I am willing to take - this is what I'd like to have! The function itself has nothing to validate, because it knows it can only get valid positive integers - they cannot be constructed without validation. You'd have to run your validation only once - upon creation of the type! Think of other cases, where validation might be more expensive (validate an email address or URL, or that a number is a prime).
Advantages:
Your API tells you directly what kind of objects you accept (no more do(String, String, String) what could be do(User, Email, Password))
Your objects get validated "automatically"
The compiler can help you reduce the risk of bugs. Some things that you'd before see on run time can be seen on compile time. Example:
def makeNegative(i: PositiveInt): NegativeInt = -i
addOne(makeNegative(1)) //will create a compile-time error!
However, there are some drawbacks:
Unfortunately, you break many functions that work due to implicit conversions. E.g., this will not work:
val i: PositiveInteger = 5
val range = i to 10 //error: value to is not a member of this.MyStuff.PositiveInt
val range = i.value to 10 //will work
It could be solved if you could extend Int and just add the require, because then all PositiveInt are Ints (what really is the case!), but Int is final :). You could add implicit conversions for all the cases you need, but that would be pretty verbose.
More objects are created. Maybe one can lower that burden with value classes (can anybody show me how?).
These are my questions:
Am I missing something? I have not seen anybody do this before, and I wonder why. Maybe there are good reasons for not doing this.
Is there a better way to integrate validation into my types?
How can I avoid the problems with the need of duplicate implicits (drawback #1)? Maybe some kind of macro that looks at other implicits in scope and adds implicits at compile time for me (Example: implicit conversion from PositiveInt to RichInt)?
You can create a class with a private constructor visible to a companion object with a factory method e.g.
class PositiveInt private[PositiveInt](val i: Int)
object PositiveInt {
def apply(i: Int): Option[PositiveInt] = if(i > 0) Some(new PositiveInt(i)) else None
}
clients cannot create instances of PositiveInt directly so they have to go through the apply method which does the validation and only returns valid instances if the input value is valid.

Expressing square in Scala

For some reason (that escapes me), Scala math library does not have a pow-function for integers, but only for Doubles.
I need a square function for integers and was figuring what might be the usual way to do this in Scala.
object TestX extends App {
def pow2(v: Int)= v*v
//class MyRichInt( val v: Int ) {
// def ² : Int = v*v // says: "illegal character" for UTF-8 power-of-two
//}
println( pow2(42) )
//println( 42² )
println( math.pow(42,2).toInt )
}
I was surprised to see that the '²' character is not liked by Scala. Maybe it's taken to be a number? Usually all kinds of weird Unicode values are valid and using 42² in code would, indeed, be fancy.
Never mind. Should I shut up and just start using my own pow2 function?
Yes, use your own pow2. If you need higher powers, you probably won't have room in an Int anyway. Consider using BigInt.pow:
scala> BigInt(40).pow(40)
res0: scala.math.BigInt = 12089258196146291747061760000000000000000000000000000000000000000
Of course, if you need not N2 but 2N, just use shifts. (1 << k = 2k) These work with BigInt also.
Use backticks for Unicode characters, and implicit classes (Scala 2.10) to add operation on arbitrary types:
implicit class PowerInt(i: Int) {
def `²`: Int = i * i
}
Usage:
3 `²`
Result:
9

Why is implicit transformation of numerical types inconsistent between "for/comprehension" expressions compared to(!) assignment operations?

Why is desugaring and implicit transformation of numerical types inconsistent between "for/comprehension" expressions compared to(!) assignment operations?
I'm sure there are many general perspectives on this but I couldn't figure out a concise and logical explanation for the current behavior. [Ref:"Behavior of Scala for/comprehension..."
]
For the sake of correctness all translations below was generated with the scala compiler ("scalac -Xprint:typer -e")
For example, during implicit numeric assignment transformation the Destination type is dominant:
Source: var l:Long = 0
Result : val l: Long = 0L
Source: var l:Long = 0.toInt
Result : var l: Long = 0.toInt.toLong
During implicit transformation of "for/comprehension" expressions the Source type is dominant:
Source: for (i:Long <- 0 to 1000000000L) { }
Result : 0.to(1000000000L).foreach(((i: Long) => ()))
Source: for (i <- 0L to 1000000000L) { }
Result : scala.this.Predef.longWrapper(0L).to(1000000000L).foreach[Unit](((i: Long) => ()))
There are two completely different things going on. First, assignment:
val l: Long = 0
We have an Int that is being assigned to a Long. That shouldn't be possible, unless there is an implicit conversion from Int to Long, which we can verify like this:
scala> implicitly[Int => Long]
res1: Int => Long = <function1>
Since there is such a conversion, that conversion is applied.
Next, the for-comprehension:
for (i:Long <- 0 to 1000000000L) { }
This doesn't work because the to method called on Int (actually called on scala.runtime.RichInt, through an implicit conversion) only admits an Int argument, not a Long argument.
The to method called on a Long (RichLong) does admit a Long argument, but there are two reasons why that doesn't apply on the expression above:
To get to RichLong's to method, the Int would have to be first converted into a Long, and then into a RichLong, and Scala does not apply two chained implicit conversions, ever. It can only convert Int to RichInt or to Long, not Int to Long to RichLong.
To apply such conversion, there would have to be some indication a Long was required in first place, and there isn't. The i: Long does not refer to the type of 0 to 1000000000L, whereas l: Long does refer to the type of 0 in the first example.
I'm not sure what you mean by "destination type" and "source type", but I don't see any problem.
If you have an Int, you can assign it to be a Long. That's fine because the range of Int is subset of the range of Long. You can't pass a Long when you expect an Int because Long has a larger range and, thus, might generate invalid Int values. And the bit about to methods is answered in your other question.
Going through your cases:
var l:Long = 0 // fine because Int up-casts to Long
var l:Long = 0.toInt // fine because Int up-casts to Long
for (i:Long <- 0 to 1000000000L) { } // bad because...
0 to 1000000000L // bad because RichInt.to doesn't accept a Long argument
for (i <- 0L to 1000000000L) { } // fine because...
0L to 1000000000L // fine because RichLong.to accepts a Long argument, and produces a Range of Longs
The cases in your for examples have nothing to do with the for. It only has to do with the fact that you are calling the to method. An explanation:
0 to 1000000000L is syntactic sugar for 0.to(1000000000L) since to is just a method. When you call to on an Int, the implicit conversion to RichInt happens, so you're really calling (new scala.runtime.RichInt(0)).to(1000000000L). But, since RichInt's to method only accepts an Int argument, passing in a Long (ie, 1000000000L) is illegal since Long cannot be cast to Int (since it might contain values that are out of Int's range.
0L to 1000000000L is syntactic sugar, again, for 0L.to(1000000000L). But now, since the to method is being called on a Long, the implicit conversion is to RichLong: (new scala.runtime.RichLong(0L)).to(1000000L). And, since RichLong's to method accepts a Long as a parameter, then everything is fine, because that's what you're giving it.
EDIT based on your comments:
It seems like your confusion here is derived from your belief that = and to should work the same way. They do not, and should not. The assignment operator, =, is a very special keyword in Scala (and any language) whereas to is not a keyword at all -- it's just a method that happens to be found on both RichInt and RichLong.
That said, there is still no inconsistency. Here's why:
Scala allows you to automatically cast up, but not down. The reason for this is very simple: if an B is is a kind of A, then an B can be substituted for a A without fear. The opposite is not true.
Lets assume you have two classes:
class A
class B extends A
val a: A = null
val b: B = null
So think about assignments:
val x: A = b // fine, since B is a subtype of A, so b *is* an A
val x: B = a // error, since As aren't necessarily Bs
Not lets look at function calls:
def f(x: A) {} // only accept As
f(b) // fine, because a B *is* an A
def g(x: B) {} // only accept Bs
g(a) // error, because As aren't necessarily Bs
So you can see, for both assignment operators and functions, you can substitute the a subtype for its supertype. If you think of Int as a kind of Long (with a more limited range), then it is perfectly analogous. Now, since methods are pretty much just functions sitting on a class, we would expect the behavior to be the same regarding arguments.
So think about what you are asking for when you say that RichInt.to(Int) should be able to accept a Long. This would mean that Scala would perform some automatic and safe conversion from Long to Int, which doesn't make sense.
FINALLY: if your real issue is that you just think that RichInt should have a method to(Long), then, well, I guess that's something to complain to the language designers about. But they'd probably just tell you to use .toLong and get on with your life.