This is the successor to my first post about Scala 3's great new type system.
Peano Axioms (again)
For fun, I'm modelling the natural numbers thus:
sealed trait Nat
case object Zero extends Nat
case class Succ[N <: Nat](n: N) extends Nat
transparent inline def toNat(inline x: Int): Nat =
inline x match
case 0 => Zero
case 1 => Succ(Zero)
case _ => Succ(toNat(x - 1))
But despite me using transparent, the function toNat just returns the super-type toNat not Succ[Succ[Succ[....
scala> toNat(3)
val res0: Succ[Nat] = Succ(Succ(Succ(Zero)))
So, with a bit of help from the guys on the Scala metaprogramming Discord channel, I came up with this:
transparent inline def toNat[I <: Int]: ToNat[I] =
inline erasedValue[I] match
case z: 0 => Zero
case s: S[t] => Succ(toNat[t])
You'll notice two major differences. First, toNat no longer takes any arguments. We use the parameterized type in lieu of a parameter
The second is this bizarre S[_] (which is imported from scala.compiletime.ops.int). It gives us the notion of a successor. Let's look more closely at it.
S Express(ion)
From the Scala source code:
/** Successor of a natural number where zero is the type 0 and successors are reduced as if the definition was:
*
* ```scala
* type S[N <: Int] <: Int = N match {
* case 0 => 1
* case 1 => 2
* case 2 => 3
* // ...
* case 2147483646 => 2147483647
* }
* ```
* @syntax markdown
*/
type S[N <: Int] <: Int
What sorcery is this? I see no implementation of that huge match statement!
PhillHenry
The bit I didn't understand was S[t] => ... I assume the compiler enforces that t must be preceeding natural number of S. Is this compiler magic? Or can anybody enforce that constraint in plain Scala?ragnar
Its compiler magic in the same way that the literal 3 is compiler magic. Basically val x: S[3] = 4 and val y: S[x.type] = 5 work as you would expect, the same holds true in reverse for pattern matching.
Compiler Options
By default, the compiler will limit the depth of recursion to 32 levels (after all, you need to tell the compiler when things have become pathological). This is far too small for us and 1024 sounds a bit more sensible. However, I use SBT and wasted an hour telling SBT exactly how to use this value. You need to add:
scalacOptions ++= Seq("-Xmax-inlines", "1024")
Note -X is broken into 2 elements of the Seq. If you foolishly try to add it as a single element as it would appear in the CLI, the compiler/SBT will ignore it.
No comments:
Post a Comment