From 15fec197c31b36cf183800fe751b5f04b10ef8ab Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Tue, 31 Jul 2012 11:52:10 -0400 Subject: [PATCH] 2.10.0-M5, different arity generalization 1. KList[M[_]] now instead of KList[HL <: HList, M[_]] a. head, tail work properly in this variant b. disadvantage is that full type not easily transformed to new type constructor 2. AList abstracts on K[L[x]], a higher order type constructor. A. Instances written for: a. KList b. Seq[M[T]] for a fixed T c. TupleN d. single values e. operate on one type constructor when nested B. Main disadvantage is type inference. It just doesn't happen for K[L[x]]. This is mitigated by AList being used internally and rarely needing to construct a K. --- util/collection/AList.scala | 211 ++++++++++++++++++ util/collection/Classes.scala | 27 +++ util/collection/INode.scala | 21 +- util/collection/KList.scala | 103 ++++----- util/collection/Settings.scala | 44 ++-- util/collection/TypeFunctions.scala | 3 +- util/collection/Types.scala | 7 +- .../collection/src/test/scala/KListTest.scala | 19 -- util/collection/src/test/scala/PMapTest.scala | 5 +- .../src/test/scala/SettingsExample.scala | 2 +- 10 files changed, 308 insertions(+), 134 deletions(-) create mode 100644 util/collection/AList.scala create mode 100644 util/collection/Classes.scala delete mode 100644 util/collection/src/test/scala/KListTest.scala diff --git a/util/collection/AList.scala b/util/collection/AList.scala new file mode 100644 index 000000000..212a58411 --- /dev/null +++ b/util/collection/AList.scala @@ -0,0 +1,211 @@ +package sbt + + import Classes.Applicative + import Types._ + +/** An abstraction over (* -> *) -> * with the purpose of abstracting over arity abstractions like KList and TupleN +* as well as homogeneous sequences Seq[M[T]]. */ +trait AList[K[L[x]] ] +{ + def transform[M[_], N[_]](value: K[M], f: M ~> N): K[N] + def traverse[M[_], N[_], P[_]](value: K[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[K[P]] + def foldr[M[_], A](value: K[M], f: (M[_], A) => A, init: A): A + + def toList[M[_]](value: K[M]): List[M[_]] = foldr[M, List[M[_]]](value, _ :: _, Nil) + def apply[M[_], C](value: K[M], f: K[Id] => C)(implicit a: Applicative[M]): M[C] = + a.map(f, traverse[M, M, Id](value, idK[M])(a)) +} +object AList +{ + type Empty = AList[({ type l[L[x]] = Unit})#l] + val empty: Empty = new Empty { + def transform[M[_], N[_]](in: Unit, f: M ~> N) = () + def foldr[M[_], T](in: Unit, f: (M[_], T) => T, init: T) = init + override def apply[M[_], C](in: Unit, f: Unit => C)(implicit app: Applicative[M]): M[C] = app.pure( f( () ) ) + def traverse[M[_], N[_], P[_]](in: Unit, f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Unit] = np.pure( () ) + } + + type SeqList[T] = AList[({ type l[L[x]] = List[L[T]] })#l] + def seq[T]: SeqList[T] = new SeqList[T] + { + def transform[M[_], N[_]](s: List[M[T]], f: M ~> N) = s.map(f.fn[T]) + def foldr[M[_], A](s: List[M[T]], f: (M[_], A) => A, init: A): A = (init /: s.reverse)( (t, m) => f(m,t)) + override def apply[M[_], C](s: List[M[T]], f: List[T] => C)(implicit ap: Applicative[M]): M[C] = + { + def loop[V](in: List[M[T]], g: List[T] => V): M[V] = + in match { + case Nil => ap.pure(g(Nil)) + case x :: xs => + val h = (ts: List[T]) => (t: T) => g(t :: ts) + ap.apply( loop(xs, h), x ) + } + loop(s, f) + } + def traverse[M[_], N[_], P[_]](s: List[M[T]], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[List[P[T]]] = ??? + } + + def klist[KL[M[_]] <: KList[M] { type Transform[N[_]] = KL[N] }]: AList[KL] = new AList[KL] { + def transform[M[_], N[_]](k: KL[M], f: M ~> N) = k.transform(f) + def foldr[M[_], T](k: KL[M], f: (M[_], T) => T, init: T): T = k.foldr(f, init) + override def apply[M[_], C](k: KL[M], f: KL[Id] => C)(implicit app: Applicative[M]): M[C] = k.apply(f)(app) + def traverse[M[_], N[_], P[_]](k: KL[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[KL[P]] = k.traverse[N,P](f)(np) + } + + type Single[A] = AList[({ type l[L[x]] = L[A]})#l] + def single[A]: Single[A] = new Single[A] { + def transform[M[_], N[_]](a: M[A], f: M ~> N) = f(a) + def foldr[M[_], T](a: M[A], f: (M[_], T) => T, init: T): T = f(a, init) + def traverse[M[_], N[_], P[_]](a: M[A], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[P[A]] = f(a) + } + + + type ASplit[K[L[x]], B[x]] = AList[ ({ type l[L[x]] = K[ (L ∙ B)#l] })#l ] + def asplit[ K[L[x]], B[x] ](base: AList[K]): ASplit[K,B] = new ASplit[K, B] + { + type Split[ L[x] ] = K[ (L ∙ B)#l ] + def transform[M[_], N[_]](value: Split[M], f: M ~> N): Split[N] = + base.transform[(M ∙ B)#l, (N ∙ B)#l](value, nestCon[M,N,B](f)) + + def traverse[M[_], N[_], P[_]](value: Split[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Split[P]] = + { + val g = nestCon[M, (N ∙ P)#l, B](f) + base.traverse[(M ∙ B)#l, N, (P ∙ B)#l](value, g)(np) + } + + def foldr[M[_], A](value: Split[M], f: (M[_], A) => A, init: A): A = + base.foldr[(M ∙ B)#l, A](value, f, init) + } + + // TODO: auto-generate + sealed trait T2K[A,B] { type l[L[x]] = (L[A], L[B]) } + type T2List[A,B] = AList[T2K[A,B]#l] + def tuple2[A, B]: T2List[A,B] = new T2List[A,B] + { + type T2[M[_]] = (M[A], M[B]) + def transform[M[_], N[_]](t: T2[M], f: M ~> N): T2[N] = (f(t._1), f(t._2)) + def foldr[M[_], T](t: T2[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, init)) + def traverse[M[_], N[_], P[_]](t: T2[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T2[P]] = + { + val g = (Tuple2.apply[P[A], P[B]] _).curried + np.apply( np.map(g, f(t._1)), f(t._2) ) + } + } + + sealed trait T3K[A,B,C] { type l[L[x]] = (L[A], L[B], L[C]) } + type T3List[A,B,C] = AList[T3K[A,B,C]#l] + def tuple3[A, B, C]: T3List[A,B,C] = new T3List[A,B,C] + { + type T3[M[_]] = (M[A], M[B], M[C]) + def transform[M[_], N[_]](t: T3[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3)) + def foldr[M[_], T](t: T3[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, init))) + def traverse[M[_], N[_], P[_]](t: T3[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T3[P]] = + { + val g = (Tuple3.apply[P[A],P[B],P[C]] _).curried + np.apply( np.apply( np.map(g, f(t._1)), f(t._2) ), f(t._3) ) + } + } + + sealed trait T4K[A,B,C,D] { type l[L[x]] = (L[A], L[B], L[C], L[D]) } + type T4List[A,B,C,D] = AList[T4K[A,B,C,D]#l] + def tuple4[A, B, C, D]: T4List[A,B,C,D] = new T4List[A,B,C,D] + { + type T4[M[_]] = (M[A], M[B], M[C], M[D]) + def transform[M[_], N[_]](t: T4[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4)) + def foldr[M[_], T](t: T4[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, init)))) + def traverse[M[_], N[_], P[_]](t: T4[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T4[P]] = + { + val g = (Tuple4.apply[P[A], P[B], P[C], P[D]] _).curried + np.apply( np.apply( np.apply( np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)) + } + } + + sealed trait T5K[A,B,C,D,E] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E]) } + type T5List[A,B,C,D,E] = AList[T5K[A,B,C,D,E]#l] + def tuple5[A, B, C, D, E]: T5List[A,B,C,D,E] = new T5List[A,B,C,D,E] { + type T5[M[_]] = (M[A], M[B], M[C], M[D], M[E]) + def transform[M[_], N[_]](t: T5[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5)) + def foldr[M[_], T](t: T5[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, init))))) + def traverse[M[_], N[_], P[_]](t: T5[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T5[P]] = + { + val g = (Tuple5.apply[P[A],P[B],P[C],P[D],P[E]] _ ).curried + np.apply( np.apply( np.apply( np.apply( np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5) ) + } + } + + sealed trait T6K[A,B,C,D,E,F] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F]) } + type T6List[A,B,C,D,E,F] = AList[T6K[A,B,C,D,E,F]#l] + def tuple6[A, B, C, D, E, F]: T6List[A,B,C,D,E,F] = new T6List[A,B,C,D,E,F] { + type T6[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F]) + def transform[M[_], N[_]](t: T6[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6)) + def foldr[M[_], T](t: T6[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, init)))))) + def traverse[M[_], N[_], P[_]](t: T6[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T6[P]] = + { + val g = (Tuple6.apply[P[A],P[B],P[C],P[D],P[E],P[F]] _ ).curried + np.apply( np.apply( np.apply( np.apply( np.apply( np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)) + } + } + + sealed trait T7K[A,B,C,D,E,F,G] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G]) } + type T7List[A,B,C,D,E,F,G] = AList[T7K[A,B,C,D,E,F,G]#l] + def tuple7[A,B,C,D,E,F,G]: T7List[A,B,C,D,E,F,G] = new T7List[A,B,C,D,E,F,G] { + type T7[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G]) + def transform[M[_], N[_]](t: T7[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7)) + def foldr[M[_], T](t: T7[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, init))))))) + def traverse[M[_], N[_], P[_]](t: T7[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T7[P]] = + { + val g = (Tuple7.apply[P[A],P[B],P[C],P[D],P[E],P[F],P[G]] _ ).curried + np.apply( np.apply( np.apply( np.apply( np.apply( np.apply( np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)) + } + } + sealed trait T8K[A,B,C,D,E,F,G,H] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H]) } + type T8List[A,B,C,D,E,F,G,H] = AList[T8K[A,B,C,D,E,F,G,H]#l] + def tuple8[A,B,C,D,E,F,G,H]: T8List[A,B,C,D,E,F,G,H] = new T8List[A,B,C,D,E,F,G,H] { + type T8[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H]) + def transform[M[_], N[_]](t: T8[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8)) + def foldr[M[_], T](t: T8[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, init)))))))) + def traverse[M[_], N[_], P[_]](t: T8[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T8[P]] = + { + val g = (Tuple8.apply[P[A],P[B],P[C],P[D],P[E],P[F],P[G],P[H]] _ ).curried + np.apply( np.apply( np.apply( np.apply( np.apply( np.apply( np.apply( np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)) + } + } + + sealed trait T9K[A,B,C,D,E,F,G,H,I] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I]) } + type T9List[A,B,C,D,E,F,G,H,I] = AList[T9K[A,B,C,D,E,F,G,H,I]#l] + def tuple9[A,B,C,D,E,F,G,H,I]: T9List[A,B,C,D,E,F,G,H,I] = new T9List[A,B,C,D,E,F,G,H,I] { + type T9[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I]) + def transform[M[_], N[_]](t: T9[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9)) + def foldr[M[_], T](t: T9[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, init))))))))) + def traverse[M[_], N[_], P[_]](t: T9[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T9[P]] = + { + val g = (Tuple9.apply[P[A],P[B],P[C],P[D],P[E],P[F],P[G],P[H],P[I]] _ ).curried + np.apply( np.apply( np.apply( np.apply( np.apply( np.apply( np.apply( np.apply( np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)) + } + } + + sealed trait T10K[A,B,C,D,E,F,G,H,I,J] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I], L[J]) } + type T10List[A,B,C,D,E,F,G,H,I,J] = AList[T10K[A,B,C,D,E,F,G,H,I,J]#l] + def tuple10[A,B,C,D,E,F,G,H,I,J]: T10List[A,B,C,D,E,F,G,H,I,J] = new T10List[A,B,C,D,E,F,G,H,I,J] { + type T10[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I], M[J]) + def transform[M[_], N[_]](t: T10[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9), f(t._10)) + def foldr[M[_], T](t: T10[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, f(t._10, init)))))))))) + def traverse[M[_], N[_], P[_]](t: T10[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T10[P]] = + { + val g = (Tuple10.apply[P[A],P[B],P[C],P[D],P[E],P[F],P[G],P[H],P[I],P[J]] _ ).curried + np.apply( np.apply( np.apply( np.apply( np.apply( np.apply( np.apply( np.apply( np.apply( np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)), f(t._10)) + } + } + + sealed trait T11K[A,B,C,D,E,F,G,H,I,J,K] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I], L[J], L[K]) } + type T11List[A,B,C,D,E,F,G,H,I,J,K] = AList[T11K[A,B,C,D,E,F,G,H,I,J,K]#l] + def tuple11[A,B,C,D,E,F,G,H,I,J,K]: T11List[A,B,C,D,E,F,G,H,I,J,K] = new T11List[A,B,C,D,E,F,G,H,I,J,K] { + type T11[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I], M[J], M[K]) + def transform[M[_], N[_]](t: T11[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9), f(t._10), f(t._11)) + def foldr[M[_], T](t: T11[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, f(t._10, f(t._11,init))))))))))) + def traverse[M[_], N[_], P[_]](t: T11[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T11[P]] = + { + val g = (Tuple11.apply[P[A],P[B],P[C],P[D],P[E],P[F],P[G],P[H],P[I],P[J],P[K]] _ ).curried + np.apply( np.apply( np.apply( np.apply( np.apply( np.apply( np.apply( np.apply( np.apply( np.apply( np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)), f(t._10)), f(t._11)) + } + } +} diff --git a/util/collection/Classes.scala b/util/collection/Classes.scala new file mode 100644 index 000000000..74796c829 --- /dev/null +++ b/util/collection/Classes.scala @@ -0,0 +1,27 @@ +package sbt + +object Classes +{ + trait Applicative[M[_]] + { + def apply[S,T](f: M[S => T], v: M[S]): M[T] + def pure[S](s: => S): M[S] + def map[S, T](f: S => T, v: M[S]): M[T] + } + trait Monad[M[_]] extends Applicative[M] + { + def flatten[T](m: M[M[T]]): M[T] + } + implicit val optionMonad: Monad[Option] = new Monad[Option] { + def apply[S,T](f: Option[S => T], v: Option[S]) = (f, v) match { case (Some(fv), Some(vv)) => Some(fv(vv)); case _ => None } + def pure[S](s: => S) = Some(s) + def map[S, T](f: S => T, v: Option[S]) = v map f + def flatten[T](m: Option[Option[T]]): Option[T] = m.flatten + } + implicit val listMonad: Monad[List] = new Monad[List] { + def apply[S,T](f: List[S => T], v: List[S]) = for(fv <- f; vv <- v) yield fv(vv) + def pure[S](s: => S) = s :: Nil + def map[S, T](f: S => T, v: List[S]) = v map f + def flatten[T](m: List[List[T]]): List[T] = m.flatten + } +} \ No newline at end of file diff --git a/util/collection/INode.scala b/util/collection/INode.scala index 0e48324e3..1ac9152a2 100644 --- a/util/collection/INode.scala +++ b/util/collection/INode.scala @@ -3,7 +3,7 @@ package sbt import java.lang.Runnable import java.util.concurrent.{atomic, Executor, LinkedBlockingQueue} import atomic.{AtomicBoolean, AtomicInteger} - import Types.{:+:, Id} + import Types.{:+:, ConstK, Id} object EvaluationState extends Enumeration { val New, Blocked, Ready, Calling, Evaluated = Value @@ -24,8 +24,7 @@ abstract class EvaluateSettings[Scope] private[this] val transform: Initialize ~> INode = new (Initialize ~> INode) { def apply[T](i: Initialize[T]): INode[T] = i match { case k: Keyed[s, T] => single(getStatic(k.scopedKey), k.transform) - case a: Apply[hl,T] => new MixedNode(a.inputs transform transform, a.f) - case u: Uniform[s, T] => new UniformNode(u.inputs map transform.fn[s], u.f) + case a: Apply[k,T] => new MixedNode[k,T]( a.alist.transform[Initialize, INode](a.inputs, transform), a.f, a.alist) case b: Bind[s,T] => new BindNode[s,T]( transform(b.in), x => transform(b.f(x))) case v: Value[T] => constant(v.value) case o: Optional[s,T] => o.a match { @@ -155,8 +154,9 @@ abstract class EvaluateSettings[Scope] protected def dependsOn: Seq[INode[_]] protected def evaluate0(): Unit } - private[this] def constant[T](f: () => T): INode[T] = new MixedNode[HNil, T](KNil, _ => f()) - private[this] def single[S,T](in: INode[S], f: S => T): INode[T] = new MixedNode[S :+: HNil, T](in :^: KNil, hl => f(hl.head)) + + private[this] def constant[T](f: () => T): INode[T] = new MixedNode[ConstK[Unit]#l, T]((), _ => f(), AList.empty) + private[this] def single[S,T](in: INode[S], f: S => T): INode[T] = new MixedNode[ ({ type l[L[x]] = L[S] })#l, T](in, f, AList.single[S]) private[this] final class BindNode[S,T](in: INode[S], f: S => INode[T]) extends INode[T] { protected def dependsOn = in :: Nil @@ -166,14 +166,9 @@ abstract class EvaluateSettings[Scope] setValue(value) } } - private[this] final class UniformNode[S,T](in: Seq[INode[S]], f: Seq[S] => T) extends INode[T] + private[this] final class MixedNode[K[L[x]], T](in: K[INode], f: K[Id] => T, alist: AList[K]) extends INode[T] { - protected def dependsOn = in - protected def evaluate0(): Unit = setValue( f(in.map(_.get)) ) - } - private[this] final class MixedNode[HL <: HList, T](in: KList[INode, HL], f: HL => T) extends INode[T] - { - protected def dependsOn = in.toList - protected def evaluate0(): Unit = setValue( f( in down getValue ) ) + protected def dependsOn = alist.toList(in) + protected def evaluate0(): Unit = setValue( f( alist.transform(in, getValue) ) ) } } diff --git a/util/collection/KList.scala b/util/collection/KList.scala index 7b58aca32..70e3852f9 100644 --- a/util/collection/KList.scala +++ b/util/collection/KList.scala @@ -1,76 +1,49 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ package sbt -import Types._ + import Types._ + import Classes.Applicative -/** A higher-order heterogeneous list. It has a type constructor M[_] and -* type parameters HL. The underlying data is M applied to each type parameter. -* Explicitly tracking M[_] allows performing natural transformations or ensuring -* all data conforms to some common type. -* -* For background, see -* http://apocalisp.wordpress.com/2010/11/01/type-level-programming-in-scala-part-8a-klist%C2%A0motivation/ - */ -sealed trait KList[+M[_], HL <: HList] +/** Heterogeneous list with each element having type M[T] for some type T.*/ +sealed trait KList[+M[_]] { - type Raw = HL - /** Transform to the underlying HList type.*/ - def down(implicit ev: M ~> Id): HL - /** Apply a natural transformation. */ - def transform[N[_]](f: M ~> N): KList[N, HL] - /** Convert to a List. */ + type Transform[N[_]] <: KList[N] + + /** Apply the natural transformation `f` to each element. */ + def transform[N[_]](f: M ~> N): Transform[N] + + def foldr[T](f: (M[_], T) => T, init: T): T = init // had trouble defining it in KNil + def apply[N[x] >: M[x], Z](f: Transform[Id] => Z)(implicit ap: Applicative[N]): N[Z] + def traverse[N[_], P[_]](f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Transform[P]] def toList: List[M[_]] - /** Convert to an HList. */ - def combine[N[X] >: M[X]]: HL#Wrap[N] - - def foldr[P[_ <: HList],N[X] >: M[X]](f: KFold[N,P]): P[HL] } -trait KFold[M[_],P[_ <: HList]] +final case class KCons[H, +T <: KList[M], +M[_]](head: M[H], tail: T) extends KList[M] { - def kcons[H,T <: HList](h: M[H], acc: P[T]): P[H :+: T] - def knil: P[HNil] -} + final type Transform[N[_]] = KCons[H, tail.Transform[N], N] -final case class KCons[H, T <: HList, +M[_]](head: M[H], tail: KList[M,T]) extends KList[M, H :+: T] + def transform[N[_]](f: M ~> N) = KCons(f(head), tail.transform(f)) + def toList: List[M[_]] = head :: tail.toList + def apply[N[x] >: M[x], Z](f: Transform[Id] => Z)(implicit ap: Applicative[N]): N[Z] = + { + val g = (t: tail.Transform[Id]) => (h: H) =>f( KCons[H, tail.Transform[Id], Id](h, t) ) + ap.apply( tail.apply[N, H => Z](g), head ) + } + def traverse[N[_], P[_]](f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Transform[P]] = + { + val tt: N[tail.Transform[P]] = tail.traverse[N,P](f) + val g = (t: tail.Transform[P]) => (h: P[H]) => KCons(h, t) + np.apply(np.map(g, tt), f(head)) + } + def :^:[A,N[x] >: M[x]](h: N[A]) = KCons(h, this) + override def foldr[T](f: (M[_], T) => T, init: T): T = f(head, tail.foldr(f, init)) +} +sealed abstract class KNil extends KList[Nothing] { - def down(implicit f: M ~> Id) = HCons(f(head), tail down f) - def transform[N[_]](f: M ~> N) = KCons( f(head), tail transform f ) - // prepend - def :^: [N[X] >: M[X], G](g: N[G]) = KCons(g, this) - def toList = head :: tail.toList - - def combine[N[X] >: M[X]]: (H :+: T)#Wrap[N] = HCons(head, tail.combine) - - override def toString = head + " :^: " + tail.toString - - def foldr[P[_ <: HList],N[X] >: M[X]](f: KFold[N,P]) = f.kcons(head, tail foldr f) + final type Transform[N[_]] = KNil + final def transform[N[_]](f: Nothing ~> N): Transform[N] = KNil + final def toList = Nil + final def apply[N[x], Z](f: KNil => Z)(implicit ap: Applicative[N]): N[Z] = ap.pure(f(KNil)) + final def traverse[N[_], P[_]](f: Nothing ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[KNil] = np.pure(KNil) } - -sealed class KNil extends KList[Nothing, HNil] -{ - def down(implicit f: Nothing ~> Id) = HNil - def transform[N[_]](f: Nothing ~> N) = KNil - def :^: [M[_], H](h: M[H]) = KCons(h, this) - def toList = Nil - def combine[N[X]] = HNil - override def foldr[P[_ <: HList],N[_]](f: KFold[N,P]) = f.knil - override def toString = "KNil" -} -object KNil extends KNil - -object KList -{ - // nicer alias for pattern matching - val :^: = KCons - - def fromList[M[_]](s: Seq[M[_]]): KList[M, _ <: HList] = if(s.isEmpty) KNil else KCons(s.head, fromList(s.tail)) - - // haven't found a way to convince scalac that KList[M, H :+: T] implies KCons[H,T,M] - // Therefore, this method exists to put the cast in one location. - implicit def kcons[H, T <: HList, M[_]](kl: KList[M, H :+: T]): KCons[H,T,M] = - kl.asInstanceOf[KCons[H,T,M]] - // haven't need this, but for symmetry with kcons: - implicit def knil[M[_]](kl: KList[M, HNil]): KNil = KNil +case object KNil extends KNil { + def :^:[M[_], H](h: M[H]): KCons[H, KNil, M] = KCons(h, this) } diff --git a/util/collection/Settings.scala b/util/collection/Settings.scala index 6df7291af..981096c0b 100644 --- a/util/collection/Settings.scala +++ b/util/collection/Settings.scala @@ -61,10 +61,12 @@ trait Init[Scope] def setting[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition = NoPosition): Setting[T] = new Setting[T](key, init, pos) def value[T](value: => T): Initialize[T] = new Value(value _) def optional[T,U](i: Initialize[T])(f: Option[T] => U): Initialize[U] = new Optional(Some(i), f) - def update[T](key: ScopedKey[T])(f: T => T): Setting[T] = new Setting[T](key, app(key :^: KNil)(hl => f(hl.head)), NoPosition) + def update[T](key: ScopedKey[T])(f: T => T): Setting[T] = new Setting[T](key, map(key)(f), NoPosition) def bind[S,T](in: Initialize[S])(f: S => Initialize[T]): Initialize[T] = new Bind(f, in) - def app[HL <: HList, T](inputs: KList[Initialize, HL])(f: HL => T): Initialize[T] = new Apply(f, inputs) - def uniform[S,T](inputs: Seq[Initialize[S]])(f: Seq[S] => T): Initialize[T] = new Uniform(f, inputs) + def map[S,T](in: Initialize[S])(f: S => T): Initialize[T] = new Apply[ ({ type l[L[x]] = L[S] })#l, T](f, in, AList.single[S]) + def app[K[L[x]], T](inputs: K[Initialize])(f: K[Id] => T)(implicit alist: AList[K]): Initialize[T] = new Apply[K, T](f, inputs, alist) + def uniform[S,T](inputs: Seq[Initialize[S]])(f: Seq[S] => T): Initialize[T] = + new Apply[({ type l[L[x]] = List[L[S]] })#l, T](f, inputs.toList, AList.seq[S]) def empty(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = new Settings0(Map.empty, delegates) def asTransform(s: Settings[Scope]): ScopedKey ~> Id = new (ScopedKey ~> Id) { @@ -219,11 +221,12 @@ trait Init[Scope] def apply[S](g: T => S): Initialize[S] def mapReferenced(g: MapScoped): Initialize[T] def validateReferenced(g: ValidateRef): ValidatedInit[T] - def zip[S](o: Initialize[S]): Initialize[(T,S)] = zipWith(o)((x,y) => (x,y)) - def zipWith[S,U](o: Initialize[S])(f: (T,S) => U): Initialize[U] = - new Apply[T :+: S :+: HNil, U]( { case t :+: s :+: HNil => f(t,s)}, this :^: o :^: KNil) def mapConstant(g: MapConstant): Initialize[T] def evaluate(map: Settings[Scope]): T + def zip[S](o: Initialize[S]): Initialize[(T,S)] = zipTupled(o)(idFun) + def zipWith[S,U](o: Initialize[S])(f: (T,S) => U): Initialize[U] = zipTupled(o)(f.tupled) + private[this] def zipTupled[S,U](o: Initialize[S])(f: ((T,S)) => U): Initialize[U] = + new Apply[({ type l[L[x]] = (L[T], L[S]) })#l, U](f, (this, o), AList.tuple2[T,S]) } object Initialize { @@ -330,34 +333,21 @@ trait Init[Scope] def mapConstant(g: MapConstant) = this def evaluate(map: Settings[Scope]): T = value() } - private[sbt] final class Apply[HL <: HList, T](val f: HL => T, val inputs: KList[Initialize, HL]) extends Initialize[T] + private[sbt] final class Apply[K[L[x]], T](val f: K[Id] => T, val inputs: K[Initialize], val alist: AList[K]) extends Initialize[T] { - def dependencies = deps(inputs.toList) + def dependencies = deps(alist.toList(inputs)) def mapReferenced(g: MapScoped) = mapInputs( mapReferencedT(g) ) - def apply[S](g: T => S) = new Apply(g compose f, inputs) + def apply[S](g: T => S) = new Apply(g compose f, inputs, alist) def mapConstant(g: MapConstant) = mapInputs( mapConstantT(g) ) - def mapInputs(g: Initialize ~> Initialize): Initialize[T] = new Apply(f, inputs transform g) - def evaluate(ss: Settings[Scope]) = f(inputs down evaluateT(ss)) + def mapInputs(g: Initialize ~> Initialize): Initialize[T] = new Apply(f, alist.transform(inputs, g), alist) + def evaluate(ss: Settings[Scope]) = f(alist.transform(inputs, evaluateT(ss))) def validateReferenced(g: ValidateRef) = { - val tx = inputs transform validateReferencedT(g) - val undefs = tx.toList.flatMap(_.left.toSeq.flatten) + val tx = alist.transform(inputs, validateReferencedT(g)) + val undefs = alist.toList(tx).flatMap(_.left.toSeq.flatten) val get = new (ValidatedInit ~> Initialize) { def apply[T](vr: ValidatedInit[T]) = vr.right.get } - if(undefs.isEmpty) Right(new Apply(f, tx transform get)) else Left(undefs) + if(undefs.isEmpty) Right(new Apply(f, alist.transform(tx, get), alist)) else Left(undefs) } } - private[sbt] final class Uniform[S, T](val f: Seq[S] => T, val inputs: Seq[Initialize[S]]) extends Initialize[T] - { - def dependencies = deps(inputs) - def mapReferenced(g: MapScoped) = new Uniform(f, inputs map mapReferencedT(g).fn) - def validateReferenced(g: ValidateRef) = - { - val (undefs, ok) = Util.separateE(inputs map validateReferencedT(g).fn ) - if(undefs.isEmpty) Right( new Uniform(f, ok) ) else Left(undefs.flatten) - } - def apply[S](g: T => S) = new Uniform(g compose f, inputs) - def mapConstant(g: MapConstant) = new Uniform(f, inputs map mapConstantT(g).fn) - def evaluate(ss: Settings[Scope]) = f(inputs map evaluateT(ss).fn ) - } private def remove[T](s: Seq[T], v: T) = s filterNot (_ == v) } diff --git a/util/collection/TypeFunctions.scala b/util/collection/TypeFunctions.scala index bbfad9b8e..6a4978750 100644 --- a/util/collection/TypeFunctions.scala +++ b/util/collection/TypeFunctions.scala @@ -7,6 +7,7 @@ trait TypeFunctions { type Id[X] = X sealed trait Const[A] { type Apply[B] = A } + sealed trait ConstK[A] { type l[L[x]] = A } sealed trait Compose[A[_], B[_]] { type Apply[T] = A[B[T]] } sealed trait ∙[A[_], B[_]] { type l[T] = A[B[T]] } sealed trait P1of2[M[_,_], A] { type Apply[B] = M[A,B]; type Flip[B] = M[B, A] } @@ -16,6 +17,7 @@ trait TypeFunctions final val some = new (Id ~> Some) { def apply[T](t: T) = Some(t) } final def idFun[T] = (t: T) => t final def const[A,B](b: B): A=> B = _ => b + final def idK[M[_]]: M ~> M = new (M ~> M) { def apply[T](m: M[T]): M[T] = m } def nestCon[M[_], N[_], G[_]](f: M ~> N): (M ∙ G)#l ~> (N ∙ G)#l = f.asInstanceOf[(M ∙ G)#l ~> (N ∙ G)#l] // implemented with a cast to avoid extra object+method call. castless version: @@ -26,7 +28,6 @@ trait TypeFunctions implicit def toFn1[A,B](f: A => B): Fn1[A,B] = new Fn1[A,B] { def ∙[C](g: C => A) = f compose g } - def idK[M[_]]: M ~> M = new (M ~> M) { def apply[T](m: M[T]): M[T] = m } type Endo[T] = T=>T type ~>|[A[_],B[_]] = A ~> Compose[Option, B]#Apply diff --git a/util/collection/Types.scala b/util/collection/Types.scala index 42b81f990..d3a3420b0 100644 --- a/util/collection/Types.scala +++ b/util/collection/Types.scala @@ -4,15 +4,10 @@ package sbt object Types extends Types -{ - implicit def hconsToK[M[_], H, T <: HList](h: M[H] :+: T)(implicit mt: T => KList[M, T]): KList[M, H :+: T] = - KCons[H, T, M](h.head, mt(h.tail) ) - implicit def hnilToK(hnil: HNil): KNil = KNil -} trait Types extends TypeFunctions { val :^: = KCons - val :+: = HCons type :+:[H, T <: HList] = HCons[H,T] + val :+: = HCons } diff --git a/util/collection/src/test/scala/KListTest.scala b/util/collection/src/test/scala/KListTest.scala deleted file mode 100644 index 2ca25a31a..000000000 --- a/util/collection/src/test/scala/KListTest.scala +++ /dev/null @@ -1,19 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package sbt - -import Types._ - -object KTest { - val f = new (Option ~> List) { def apply[T](o: Option[T]): List[T] = o.toList } - - val x = Some(3) :^: Some("asdf") :^: KNil - val y = x transform f - val m1a = y match { case List(3) :^: List("asdf") :^: KNil => println("true") } - val m1b = (List(3) :^: KNil) match { case yy :^: KNil => println("true") } - - val head = new (List ~> Id) { def apply[T](xs: List[T]): T = xs.head } - val z = y down head - val m2 = z match { case 3 :+: "asdf" :+: HNil => println("true") } -} diff --git a/util/collection/src/test/scala/PMapTest.scala b/util/collection/src/test/scala/PMapTest.scala index bac4b7364..7970e175e 100644 --- a/util/collection/src/test/scala/PMapTest.scala +++ b/util/collection/src/test/scala/PMapTest.scala @@ -13,6 +13,7 @@ object PMapTest mp(Some(3)) = 9 val x = Some(3) :^: Some("asdf") :^: KNil val y = x.transform[Id](mp) - val z = y.down - z match { case 9 :+: "a" :+: HNil => println("true") } + assert(y.head == 9) + assert(y.tail.head == "a") + assert(y.tail.tail == KNil) } \ No newline at end of file diff --git a/util/collection/src/test/scala/SettingsExample.scala b/util/collection/src/test/scala/SettingsExample.scala index 558de7f4a..637f0ad51 100644 --- a/util/collection/src/test/scala/SettingsExample.scala +++ b/util/collection/src/test/scala/SettingsExample.scala @@ -49,7 +49,7 @@ object SettingsUsage // Define some settings val mySettings: Seq[Setting[_]] = Seq( setting( a3, value( 3 ) ), - setting( b4, app(a4 :^: KNil) { case av :+: HNil => av * 3 } ), + setting( b4, map(a4)(_ * 3)), update(a5)(_ + 1) )