I was thinking whether it would be possible in Scala to define a type like NegativeNumber
. This type would represent a negative number and it would be checked by the compiler similarly to Ints, Strings etc.
val x: NegativeNumber = -34
val y: NegativeNumber = 34 // should not compile
Likewise:
val s: ContainsHello = "hello world"
val s: ContainsHello = "foo bar" // this should not compile either
I could use these types just like other types, eg:
def myFunc(x: ContainsHello): Unit = println(s"$x contains hello")
These constrained types could be backed by casual types (Int, String).
Is it possible to implement these types (maybe with macros)?
How about custom literals?
val neg = -34n //neg is of type NegativeNumber because of the suffix
val pos = 34n // compile error
Unfortunately, no this isn't something you could easily check at compile time. Well - at least not if you aren't restricting the operations on your type. If your goal is simply to check that a number literal is non-zero, you could easily write a macro that checks this property. However, I do not see any benefit in proving that a negative literal is indeed negative.
The problem isn't a limitation of Scala - which has a very strong type system - but the fact that (in a reasonably complex program) you can't statically know every possible state. You can however try to overapproximate the set of all possible states.
Let us consider the example of introducing a type NegativeNumber
that only ever represents a negative number. For simplicity, we define only one operation: plus
.
Say you would only allow addition of multiple NegativeNumber
, then, the type system could be used to guarantee that each NegativeNumber
is indeed a negative number. But this seems really restrictive, so a useful example would certainly allow us to add at least a NegativeNumber
and a general Int
.
What if you had an expression val z: NegativeNumber = plus(x, y)
where you don't know the value of x
and y
statically (maybe they are returned by a function). How do you know (statically) that z
is indead a negative number?
An approach to solve the problem would be to introduce Abstract Interpretation which must be run on a representation of your program (Source Code, Abstract Syntax Tree, ...).
For example, you could define a Lattice on the numbers with the following elements:
Top
: all numbers+
: all positive numbers0
: the number 0
-
: all negative numbersBottom
: not a number - only introduced that each pair of elements has a greatest lower boundwith the ordering Top
> (+
, 0
, -
) > Bottom
.
Then you'd need to define semantics for your operations. Taking the commutative method plus
from our example:
plus(Bottom, something)
is always Bottom
, as you cannot calculate something using invalid numbersplus(Top, x)
, x != Bottom
is always Top
, because adding an arbitrary number to any number is always an arbitrary numberplus(+, +)
is +
, because adding two positive numbers will always yield a positive numberplus(-, -)
is -
, because adding two negative numbers will always yield a negative numberplus(0, x)
, x != Bottom
is x
, because 0
is the identity of the addition.The problem is that
plus - +
will be Top
, because you don't know if it's a positive or negative number. So to be statically safe, you'd have to take the conservative approach and disallow such an operation.
There are more sophisticated numerical domains but ultimately, they all suffer from the same problem: They represent an overapproximation to the actual program state.
I'd say the problem is similar to integer overflow/underflow: Generally, you don't know statically whether an operation exhibits an overflow - you only know this at runtime.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With