Ответ 1
Спасибо Марку Харре за выполнение этого решения. Фокус в том, что Function1
в стандартных библиотеках не определен достаточно общим образом.
Моя черта "Закрытие" в вопросе на самом деле является естественным преобразованием между функторами. Это обобщение понятия "функция".
trait ~>[F[_],G[_]] { def apply[B](f: F[B]): G[B] }
Функция a -> b
тогда должна быть специализацией этого признака, естественным преобразованием между двумя эндофункторами в категории типов Scala.
trait Const[A] { type Apply[B] = A }
type ->:[A,B] = Const[A]#Apply ~>: Const[B]#Apply
Const[A]
- функтор, который отображает каждый тип в A
.
И вот наш тип списка:
type CList[A] = ({type f[x] = Fold[A,x]})#f ~> Endo
Здесь Endo
является просто псевдонимом для типа функций, которые отображают тип на себя (endofunction).
type Endo[A] = A ->: A
И Fold
- это тип функций, которые могут складывать список:
type Fold[A,B] = A ->: Endo[B]
И наконец, вот наши конструкторы списка:
def cons[A](x: A) = {
new (CList[A] ->: CList[A]) {
def apply[C](xs: CList[A]) = new CList[A] {
def apply[B](f: Fold[A,B]) = (b: B) => f(x)(xs(f)(b))
}
}
}
def nil[A] = new CList[A] {
def apply[B](f: Fold[A,B]) = (b: B) => b
}
Одно из предостережений - необходимость прямого преобразования (A → : B) в (A = > B), чтобы помочь системе типа Scala. Так что это все еще ужасно многословно и утомительно, чтобы фактически свернуть список, когда-то созданный. Здесь эквивалент Haskell для сравнения:
nil p z = z
cons x xs p z = p x (xs p z)
Конструкция списков и сворачивание в версии Haskell является кратким и бесшумным:
let xs = cons 1 (cons 2 (cons 3 nil)) in xs (+) 0