Consider the following definition of natural numbers.
sealed trait Nat
final case object Z extends Nat
final case class S[N <: Nat]() extends Nat
And the following definition of vectors.
sealed trait Vec[N <: Nat, +A]
final case object Nil extends Vec[Z.type, Nothing]
final case class Cons[N <: Nat, +A](head: A, tail: Vec[N, A]) extends Vec[S[N], A]
Now, I defined the induction on natural numbers as follows.
trait Alg[P[_ <: Nat]] {
val z: P[Z.type]
def s[N <: Nat](p: P[N]): P[S[N]]
}
def ind[N <: Nat, P[_ <: Nat]](alg: Alg[P])(implicit indN: Alg[P] => P[N]) = indN(alg)
implicit def indZ[P[_ <: Nat]](): Alg[P] => P[Z.type] = alg => alg.z
implicit def indS[N <: Nat, P[_ <: Nat]](implicit indN: Alg[P] => P[N]): Alg[P] => P[S[N]] = alg => alg.s(indN(alg))
And, I want to use it to define a function that creates a fixed size vector.
def rep[N <: Nat, A](head: A) = {
type P[N <: Nat] = Vec[N, A]
val alg = new Alg[P] {
val z: P[Z.type] = Nil
def s[N <: Nat](tail: P[N]): P[S[N]] = Cons(head, tail)
}
ind[N, P](alg) // No implicit view available from Alg[P] => P[N]
}
val vec = rep[S[S[S[Z.type]]], Int](37) // expected Cons(37, Cons(37, Cons(37, Nil)))
Unfortunately, this doesn't work. Scala doesn't know which implicit value to use. How can I make this work?
First of all, don't put
()inimplicit def indZ[P[_ <: Nat]](). The implicitsimplicit def indZ[P[_ <: Nat]]()andimplicit def indZ[P[_ <: Nat]]are very differentHow to write an implicit Numeric for a tuple
It's convenient to introduce
type Z = Z.type. Then you can write justZinstead ofZ.type.Scala is not an actual theorem prover. Implicits are not resolved like that. If you defined
implicit def indZandimplicit def indS, Scala knows implicits (and their types)indZ,indS(indZ),indS(indS(indZ)), ... but it can't generate an implicit of an unknown lengthindS(...(indS(indZ))). So with inductive logic based on implicits you can prove theorems for a fixedN(likeZ,S[Z],S[S[Z]], ...) but not for arbitraryN(or for arbitraryNbut with implicits of a fixed length). At those places where you have arbitraryNyou should add an evidence (implicit parameter)Alg[P] => P[N], it will be checked later for a concreteN. So to say, if a method lacks an implicit (see your compile errorNo implicit view available from Alg[P] => P[N]) then you just add it as an implicit parameter:Scala: Question about shapeless to tranform HList to List
Implicit Encoder for TypedDataset and Type Bounds in Scala
How to resolve implicit lookup by bounded generic?
Typeclasses methods called in functions with type parameters
Play-JSON cannot find implicit Writes[T] for type parameter T
Why the Scala compiler can provide implicit outside of object, but cannot inside?
So try
Be careful with building your logic on implicits of functional type
Alg[P] => P[N]. Functional types play special role for implicits, they are for implicit conversions and such implicits are resolved slightly differently than implicits of all other types:Implicit view not working - is my implicit def to blame?
In scala, are there any condition where implicit view won't be able to propagate to other implicit function?
When calling a scala function with compile-time macro, how to failover smoothly when it causes compilation errors?
Scala Kleisli throws an error in IntelliJ
What are the hidden rules regarding the type inference in resolution of implicit conversions?
https://contributors.scala-lang.org/t/can-we-wean-scala-off-implicit-conversions/4388
https://contributors.scala-lang.org/t/proposed-changes-and-restrictions-for-implicit-conversions/4923
This is improved in Scala 3: https://docs.scala-lang.org/scala3/reference/contextual/conversions.html (a new type class
Conversionis introduced instead of standard functional type, although currently with more restrictions than in Scala 2, for example implicit conversions into a path-dependent type are currently impossible - But https://github.com/lampepfl/dotty/issues/7412 https://github.com/lampepfl/dotty/pull/8523).It can be better to introduce a new type class
TC[P[_ <: N], N <: Nat]and build your inductive logic onTC[P, N]rather than standard functional typesAlg[P] => P[N].It's important to know how to debug implicits (at compile time)
In scala 2 or 3, is it possible to debug implicit resolution process in runtime?