Спасибо Марку Харре за завершение этого решения. Хитрость в том, что 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