Невозможно сделать это простым способом. Типы являются гражданами во время компиляции, а числа живут во время выполнения. Посмотрим, что произойдет, если число n
будет считано с пользовательского ввода. Для разных будущих пользовательских вводов компилятор должен генерировать разные типы результатов для метода.
Если я не ошибаюсь, для этого нам понадобится язык с лучшей поддержкой зависимых типов, чем Scala. Смотрите этот вопрос: Любая причина, почему Scala не поддерживает явно зависимые типы? и особенно ответ П. Фролова там.
Тем не менее, можно выразить этот тип, если число n
известно во время компиляции. Например, это литерал Int
, final val
или какое-то простое арифметическое выражение литералов и final val
s. Например, в случае final val a = 3; wrap(Array(1,2,3), a * 2 + 1)
.
Вот пример кода класса типов, который реализует эту упаковку. Он использует библиотеку shapeless
для удобного преобразования числовых литералов в Nat
значения типа:
import scala.reflect.{classTag, ClassTag}
abstract class Wrapper[T : ClassTag, N <: Nat] {
// Type of Array[T] wrapped N times
type Out
// ClassTag of the array wrapped N times.
// It's needed to be able to wrap it one more time.
def outTag: ClassTag[Out]
// The actual function that wraps the array
def apply(array: Array[T]): Out
}
object Wrapper {
type Aux[T, N <: Nat, O] = Wrapper[T, N] { type Out = O }
// Wrap the array 0 times. The base of the recursion.
implicit def zero[T : ClassTag]: Aux[T, Nat._0, Array[T]] = new Wrapper[T, Nat._0] {
type Out = Array[T]
def outTag = classTag[T].wrap
def apply(array: Array[T]): Out = array
}
// Given a Wrapper, that wraps the array N times,
// make a Wrapper, that wraps N + 1 times.
implicit def next[T : ClassTag, N <: Nat](
implicit prev: Wrapper[T, N]
): Aux[T, Succ[N], Array[prev.Out]] = new Wrapper[T, Succ[N]] {
type Out = Array[prev.Out]
def outTag = prev.outTag.wrap
def apply(array: Array[T]): Out = Array(prev(array))(prev.outTag)
}
}
И функция обтекания, которая использует этот класс типов:
def wrap[A: ClassTag](
x: Array[A],
n: Nat
)(
implicit wrapper: Wrapper[A, n.N]
): wrapper.Out =
wrapper(x)
Компилятор знает тип результата и может использовать результат без каких-либо типов:
scala> val a = wrap(Array(1,2,3), 3)
a: Array[Array[Array[Array[Int]]]] = Array(Array(Array(Array(1, 2, 3))))
scala> a.head.head.head.sum
res1: Int = 6
scala> object Foo {
final val n = 2
def run() = wrap(Array(1,2,3), n * 2 + 1)
}
defined object Foo
scala> Foo.run()
res2: Array[Array[Array[Array[Array[Array[Int]]]]]] = Array(Array(Array(Array(Array(Array(1, 2, 3))))))