From f96b6beefc08f56218ac68b37a4cecd757cb60ee Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Fri, 6 Apr 2007 09:39:53 +0000 Subject: adding test files from tcpoly branch without hi... adding test files from tcpoly branch without history -- much faster this way, sorry --- test/files/neg/higherkind_novalue.check | 7 + test/files/neg/higherkind_novalue.scala | 4 + test/files/neg/tcpoly_bounds.check | 4 + test/files/neg/tcpoly_bounds.scala | 3 + test/files/neg/tcpoly_typealias.check | 13 ++ test/files/neg/tcpoly_typealias.scala | 47 ++++++ test/files/neg/tcpoly_variance.check | 5 + test/files/neg/tcpoly_variance.scala | 7 + test/files/neg/tcpoly_variance_enforce.check | 45 ++++++ test/files/neg/tcpoly_variance_enforce.scala | 42 +++++ test/files/neg/wellkinded_app.check | 4 + test/files/neg/wellkinded_app.scala | 4 + test/files/neg/wellkinded_app2.check | 4 + test/files/neg/wellkinded_app2.scala | 4 + test/files/neg/wellkinded_bounds.check | 4 + test/files/neg/wellkinded_bounds.scala | 3 + test/files/neg/wellkinded_wrongarity.check | 4 + test/files/neg/wellkinded_wrongarity.scala | 5 + test/files/neg/wellkinded_wrongarity2.check | 4 + test/files/neg/wellkinded_wrongarity2.scala | 4 + test/files/pos/ksbug1.scala | 4 + test/files/pos/nullary.scala | 20 +++ test/files/pos/nullary_poly.scala | 10 ++ test/files/pos/return_thistype.scala | 8 + test/files/pos/tcpoly_boundedmonad.scala | 19 +++ test/files/pos/tcpoly_bounds1.scala | 14 ++ test/files/pos/tcpoly_gm.scala | 14 ++ .../pos/tcpoly_higherorder_bound_method.scala | 3 + test/files/pos/tcpoly_late_method_params.scala | 5 + test/files/pos/tcpoly_method.scala | 6 + test/files/pos/tcpoly_overloaded.scala | 25 +++ test/files/pos/tcpoly_param_scoping.scala | 8 + test/files/pos/tcpoly_poly.scala | 3 + test/files/pos/tcpoly_return_overriding.scala | 13 ++ test/files/pos/tcpoly_seq.scala | 175 +++++++++++++++++++++ test/files/pos/tcpoly_seq_typealias.scala | 143 +++++++++++++++++ test/files/pos/tcpoly_subst.scala | 4 + test/files/pos/tcpoly_typesub.scala | 14 ++ test/files/pos/tcpoly_variance.scala | 7 + test/files/pos/tcpoly_wildcards.scala | 3 + test/files/pos/typealias_dubious.scala | 15 ++ test/files/pos/typealiases.scala | 20 +++ test/files/run/tcpoly_monads.check | 1 + test/files/run/tcpoly_monads.scala | 42 +++++ test/files/run/tcpoly_overriding.check | 1 + test/files/run/tcpoly_overriding.scala | 16 ++ test/files/run/tcpoly_parseridioms.check | 1 + test/files/run/tcpoly_parseridioms.scala | 109 +++++++++++++ 48 files changed, 920 insertions(+) create mode 100644 test/files/neg/higherkind_novalue.check create mode 100644 test/files/neg/higherkind_novalue.scala create mode 100644 test/files/neg/tcpoly_bounds.check create mode 100644 test/files/neg/tcpoly_bounds.scala create mode 100644 test/files/neg/tcpoly_typealias.check create mode 100644 test/files/neg/tcpoly_typealias.scala create mode 100644 test/files/neg/tcpoly_variance.check create mode 100644 test/files/neg/tcpoly_variance.scala create mode 100644 test/files/neg/tcpoly_variance_enforce.check create mode 100644 test/files/neg/tcpoly_variance_enforce.scala create mode 100644 test/files/neg/wellkinded_app.check create mode 100644 test/files/neg/wellkinded_app.scala create mode 100644 test/files/neg/wellkinded_app2.check create mode 100644 test/files/neg/wellkinded_app2.scala create mode 100644 test/files/neg/wellkinded_bounds.check create mode 100644 test/files/neg/wellkinded_bounds.scala create mode 100644 test/files/neg/wellkinded_wrongarity.check create mode 100644 test/files/neg/wellkinded_wrongarity.scala create mode 100644 test/files/neg/wellkinded_wrongarity2.check create mode 100644 test/files/neg/wellkinded_wrongarity2.scala create mode 100644 test/files/pos/ksbug1.scala create mode 100644 test/files/pos/nullary.scala create mode 100644 test/files/pos/nullary_poly.scala create mode 100644 test/files/pos/return_thistype.scala create mode 100644 test/files/pos/tcpoly_boundedmonad.scala create mode 100644 test/files/pos/tcpoly_bounds1.scala create mode 100644 test/files/pos/tcpoly_gm.scala create mode 100644 test/files/pos/tcpoly_higherorder_bound_method.scala create mode 100644 test/files/pos/tcpoly_late_method_params.scala create mode 100644 test/files/pos/tcpoly_method.scala create mode 100644 test/files/pos/tcpoly_overloaded.scala create mode 100644 test/files/pos/tcpoly_param_scoping.scala create mode 100644 test/files/pos/tcpoly_poly.scala create mode 100644 test/files/pos/tcpoly_return_overriding.scala create mode 100644 test/files/pos/tcpoly_seq.scala create mode 100644 test/files/pos/tcpoly_seq_typealias.scala create mode 100644 test/files/pos/tcpoly_subst.scala create mode 100644 test/files/pos/tcpoly_typesub.scala create mode 100644 test/files/pos/tcpoly_variance.scala create mode 100644 test/files/pos/tcpoly_wildcards.scala create mode 100644 test/files/pos/typealias_dubious.scala create mode 100644 test/files/pos/typealiases.scala create mode 100644 test/files/run/tcpoly_monads.check create mode 100644 test/files/run/tcpoly_monads.scala create mode 100644 test/files/run/tcpoly_overriding.check create mode 100644 test/files/run/tcpoly_overriding.scala create mode 100644 test/files/run/tcpoly_parseridioms.check create mode 100644 test/files/run/tcpoly_parseridioms.scala (limited to 'test/files') diff --git a/test/files/neg/higherkind_novalue.check b/test/files/neg/higherkind_novalue.check new file mode 100644 index 0000000000..932f7876b1 --- /dev/null +++ b/test/files/neg/higherkind_novalue.check @@ -0,0 +1,7 @@ +higherkind_novalue.scala:2: error: type m takes type parameters + val x: m // type of kind *->* doesn't classify a value, but a val/def/... can only contain/return a value + ^ +higherkind_novalue.scala:3: error: type m takes type parameters + def y: m + ^ +two errors found diff --git a/test/files/neg/higherkind_novalue.scala b/test/files/neg/higherkind_novalue.scala new file mode 100644 index 0000000000..c2e117b60e --- /dev/null +++ b/test/files/neg/higherkind_novalue.scala @@ -0,0 +1,4 @@ +abstract class HigherKind[m[s]] { + val x: m // type of kind *->* doesn't classify a value, but a val/def/... can only contain/return a value + def y: m +} \ No newline at end of file diff --git a/test/files/neg/tcpoly_bounds.check b/test/files/neg/tcpoly_bounds.check new file mode 100644 index 0000000000..03e22f9fbc --- /dev/null +++ b/test/files/neg/tcpoly_bounds.check @@ -0,0 +1,4 @@ +tcpoly_bounds.scala:3: error: type arguments [scala.List] do not conform to class A's type parameter bounds [m<: [x]>: scala.Nothing <: scala.Option[x]] +object b extends A[List] + ^ +one error found diff --git a/test/files/neg/tcpoly_bounds.scala b/test/files/neg/tcpoly_bounds.scala new file mode 100644 index 0000000000..ae1f410fac --- /dev/null +++ b/test/files/neg/tcpoly_bounds.scala @@ -0,0 +1,3 @@ +class A[m[x] <: Option[x]] +object a extends A[Some] +object b extends A[List] \ No newline at end of file diff --git a/test/files/neg/tcpoly_typealias.check b/test/files/neg/tcpoly_typealias.check new file mode 100644 index 0000000000..56912c49b7 --- /dev/null +++ b/test/files/neg/tcpoly_typealias.check @@ -0,0 +1,13 @@ +tcpoly_typealias.scala:37: error: The kind of type m does not conform to the expected kind of type m<: [+x]>: scala.Nothing <: scala.Any in trait A. +BInv.this.m's type parameters do not match type m's expected parameters: type x (in trait BInv) is invariant, but type x (in trait A) is declared covariant + type m[x] = FooCov[x] // error: invariant x in alias def + ^ +tcpoly_typealias.scala:41: error: The kind of type m does not conform to the expected kind of type m<: [+x]>: scala.Nothing <: scala.Any in trait A. +BCon.this.m's type parameters do not match type m's expected parameters: type x (in trait BCon) is contravariant, but type x (in trait A) is declared covariant + type m[-x] = FooCon[x] // error: contravariant x + ^ +tcpoly_typealias.scala:45: error: The kind of type m does not conform to the expected kind of type m<: [+x]>: scala.Nothing <: scala.Any in trait A. +BBound.this.m's type parameters do not match type m's expected parameters: type x (in trait BBound)'s bounds >: scala.Nothing <: scala.Predef.String are stricter than type x (in trait A)'s declared bounds >: scala.Nothing <: scala.Any + type m[+x <: String] = FooBound[x] // error: x with stricter bound + ^ +three errors found diff --git a/test/files/neg/tcpoly_typealias.scala b/test/files/neg/tcpoly_typealias.scala new file mode 100644 index 0000000000..6c7f80cc0b --- /dev/null +++ b/test/files/neg/tcpoly_typealias.scala @@ -0,0 +1,47 @@ +trait A { + type m[+x] +} + +trait A2 { + type m[+x <: String] +} + +trait A3 { + type m[x] +} + +trait FooCov[+x] +trait FooCon[-x] +trait FooBound[+x <: String] + +trait BOk1 extends A { + type m[+x] = FooCov[x] +} + +trait BOk2 extends A2 { + type m[+x <: String] = FooBound[x] +} + +trait BOk3 extends A2 { + type m[+x] = FooCov[x] // weaker bound +} + +trait BOk4 extends A3 { + type m[+x] = FooCov[x] // weaker variance +} + +// there are two aspects to check: + // does type alias signature (not considering RHS) correspond to abstract type member in super class + // does RHS correspond to the type alias sig +trait BInv extends A{ + type m[x] = FooCov[x] // error: invariant x in alias def +} + +trait BCon extends A{ + type m[-x] = FooCon[x] // error: contravariant x +} + +trait BBound extends A{ + type m[+x <: String] = FooBound[x] // error: x with stricter bound +} + diff --git a/test/files/neg/tcpoly_variance.check b/test/files/neg/tcpoly_variance.check new file mode 100644 index 0000000000..5fd104d607 --- /dev/null +++ b/test/files/neg/tcpoly_variance.check @@ -0,0 +1,5 @@ +tcpoly_variance.scala:6: error: error overriding method str in class A of type => m[java.lang.Object]; + method str has incompatible type => m[scala.Predef.String] + override def str: m[String] = error("foo") // since x in m[x] is invariant, ! m[String] <: m[Object] + ^ +one error found diff --git a/test/files/neg/tcpoly_variance.scala b/test/files/neg/tcpoly_variance.scala new file mode 100644 index 0000000000..8122938113 --- /dev/null +++ b/test/files/neg/tcpoly_variance.scala @@ -0,0 +1,7 @@ +class A[m[x]] { + def str: m[Object] = error("foo") +} + +class B[m[x]] extends A[m] { + override def str: m[String] = error("foo") // since x in m[x] is invariant, ! m[String] <: m[Object] +} \ No newline at end of file diff --git a/test/files/neg/tcpoly_variance_enforce.check b/test/files/neg/tcpoly_variance_enforce.check new file mode 100644 index 0000000000..c66440434b --- /dev/null +++ b/test/files/neg/tcpoly_variance_enforce.check @@ -0,0 +1,45 @@ +tcpoly_variance_enforce.scala:15: error: the kinds of the type arguments (FooInvar) do not conform to the expected kinds of the type parameters (type m) in trait coll. +FooInvar's type parameters do not match type m's expected parameters: type x (in class FooInvar) is invariant, but type x is declared covariant +object fcollinv extends coll[FooInvar] // error + ^ +tcpoly_variance_enforce.scala:16: error: the kinds of the type arguments (FooContra) do not conform to the expected kinds of the type parameters (type m) in trait coll. +FooContra's type parameters do not match type m's expected parameters: type x (in class FooContra) is contravariant, but type x is declared covariant +object fcollcon extends coll[FooContra] // error + ^ +tcpoly_variance_enforce.scala:17: error: the kinds of the type arguments (FooString) do not conform to the expected kinds of the type parameters (type m) in trait coll. +FooString's type parameters do not match type m's expected parameters: type x (in class FooString)'s bounds >: scala.Nothing <: scala.Predef.String are stricter than type x's declared bounds >: scala.Nothing <: scala.Any +object fcollwb extends coll[FooString] // error + ^ +tcpoly_variance_enforce.scala:19: error: the kinds of the type arguments (FooCov) do not conform to the expected kinds of the type parameters (type m) in trait coll2. +FooCov's type parameters do not match type m's expected parameters: type x (in class FooCov) is covariant, but type x is declared contravariant +object fcoll2ok extends coll2[FooCov] // error + ^ +tcpoly_variance_enforce.scala:20: error: the kinds of the type arguments (FooInvar) do not conform to the expected kinds of the type parameters (type m) in trait coll2. +FooInvar's type parameters do not match type m's expected parameters: type x (in class FooInvar) is invariant, but type x is declared contravariant +object fcoll2inv extends coll2[FooInvar] // error + ^ +tcpoly_variance_enforce.scala:22: error: the kinds of the type arguments (FooString) do not conform to the expected kinds of the type parameters (type m) in trait coll2. +FooString's type parameters do not match type m's expected parameters: type x (in class FooString) is covariant, but type x is declared contravarianttype x (in class FooString)'s bounds >: scala.Nothing <: scala.Predef.String are stricter than type x's declared bounds >: scala.Nothing <: scala.Any +object fcoll2wb extends coll2[FooString] // error + ^ +tcpoly_variance_enforce.scala:27: error: the kinds of the type arguments (FooString) do not conform to the expected kinds of the type parameters (type m) in trait coll3. +FooString's type parameters do not match type m's expected parameters: type x (in class FooString)'s bounds >: scala.Nothing <: scala.Predef.String are stricter than type x's declared bounds >: scala.Nothing <: scala.Any +object fcoll3wb extends coll3[FooString] // error + ^ +tcpoly_variance_enforce.scala:30: error: the kinds of the type arguments (FooString,scala.Int) do not conform to the expected kinds of the type parameters (type m,type y) in trait coll4. +FooString's type parameters do not match type m's expected parameters: type x (in class FooString)'s bounds >: scala.Nothing <: scala.Predef.String are stricter than type x's declared bounds >: scala.Nothing <: y +object fcoll4_1 extends coll4[FooString, Int] // error + ^ +tcpoly_variance_enforce.scala:31: error: the kinds of the type arguments (FooString,scala.Any) do not conform to the expected kinds of the type parameters (type m,type y) in trait coll4. +FooString's type parameters do not match type m's expected parameters: type x (in class FooString)'s bounds >: scala.Nothing <: scala.Predef.String are stricter than type x's declared bounds >: scala.Nothing <: y +object fcoll4_2 extends coll4[FooString, Any] // error + ^ +tcpoly_variance_enforce.scala:37: error: the kinds of the type arguments (FooInvar) do not conform to the expected kinds of the type parameters (type m) in trait coll. +FooInvar's type parameters do not match type m's expected parameters: type x (in class FooInvar) is invariant, but type x is declared covariant + def x: coll[FooInvar] = error("foo") // error + ^ +tcpoly_variance_enforce.scala:38: error: the kinds of the type arguments (FooContra) do not conform to the expected kinds of the type parameters (type m) in trait coll. +FooContra's type parameters do not match type m's expected parameters: type x (in class FooContra) is contravariant, but type x is declared covariant + def y: coll[FooContra] = error("foo") // error + ^ +11 errors found diff --git a/test/files/neg/tcpoly_variance_enforce.scala b/test/files/neg/tcpoly_variance_enforce.scala new file mode 100644 index 0000000000..e2fea37fff --- /dev/null +++ b/test/files/neg/tcpoly_variance_enforce.scala @@ -0,0 +1,42 @@ +trait coll[m[+x]] + +trait coll2[m[-x]] + +trait coll3[m[x]] + +trait coll4[m[x <: y], y] + +class FooInvar[x] +class FooContra[-x] +class FooCov[+x] +class FooString[+x <: String] + +object fcollok extends coll[FooCov] +object fcollinv extends coll[FooInvar] // error +object fcollcon extends coll[FooContra] // error +object fcollwb extends coll[FooString] // error + +object fcoll2ok extends coll2[FooCov] // error +object fcoll2inv extends coll2[FooInvar] // error +object fcoll2con extends coll2[FooContra] +object fcoll2wb extends coll2[FooString] // error + +object fcoll3ok extends coll3[FooCov] +object fcoll3inv extends coll3[FooInvar] +object fcoll3con extends coll3[FooContra] +object fcoll3wb extends coll3[FooString] // error + +object fcoll4ok extends coll4[FooString, String] +object fcoll4_1 extends coll4[FooString, Int] // error +object fcoll4_2 extends coll4[FooString, Any] // error + + +object test { + var ok: coll[FooCov] = _ + + def x: coll[FooInvar] = error("foo") // error + def y: coll[FooContra] = error("foo") // error +} + + +// TODO: need test for rank N with N >: 2 \ No newline at end of file diff --git a/test/files/neg/wellkinded_app.check b/test/files/neg/wellkinded_app.check new file mode 100644 index 0000000000..d57a0e4b56 --- /dev/null +++ b/test/files/neg/wellkinded_app.check @@ -0,0 +1,4 @@ +wellkinded_app.scala:3: error: x does not take type parameters + type t = x[x] + ^ +one error found diff --git a/test/files/neg/wellkinded_app.scala b/test/files/neg/wellkinded_app.scala new file mode 100644 index 0000000000..7fa3f95a98 --- /dev/null +++ b/test/files/neg/wellkinded_app.scala @@ -0,0 +1,4 @@ +// test well-kindedness checks +class WellKinded[x] { + type t = x[x] +} diff --git a/test/files/neg/wellkinded_app2.check b/test/files/neg/wellkinded_app2.check new file mode 100644 index 0000000000..20a177ea59 --- /dev/null +++ b/test/files/neg/wellkinded_app2.check @@ -0,0 +1,4 @@ +wellkinded_app2.scala:3: error: s does not take type parameters + val foo: s[Int] + ^ +one error found diff --git a/test/files/neg/wellkinded_app2.scala b/test/files/neg/wellkinded_app2.scala new file mode 100644 index 0000000000..5b73611964 --- /dev/null +++ b/test/files/neg/wellkinded_app2.scala @@ -0,0 +1,4 @@ +// test well-kindedness checks +class WellKinded[s <: Throwable] { + val foo: s[Int] +} \ No newline at end of file diff --git a/test/files/neg/wellkinded_bounds.check b/test/files/neg/wellkinded_bounds.check new file mode 100644 index 0000000000..9099934c68 --- /dev/null +++ b/test/files/neg/wellkinded_bounds.check @@ -0,0 +1,4 @@ +wellkinded_bounds.scala:2: error: class List takes type parameters +class WellKindedWrongSyntax[s <: List] { // must be s[x] <: List[x] + ^ +one error found diff --git a/test/files/neg/wellkinded_bounds.scala b/test/files/neg/wellkinded_bounds.scala new file mode 100644 index 0000000000..7b80ce1cf7 --- /dev/null +++ b/test/files/neg/wellkinded_bounds.scala @@ -0,0 +1,3 @@ +// test well-kindedness checks -- syntax error +class WellKindedWrongSyntax[s <: List] { // must be s[x] <: List[x] +} \ No newline at end of file diff --git a/test/files/neg/wellkinded_wrongarity.check b/test/files/neg/wellkinded_wrongarity.check new file mode 100644 index 0000000000..548b5e40bb --- /dev/null +++ b/test/files/neg/wellkinded_wrongarity.check @@ -0,0 +1,4 @@ +wellkinded_wrongarity.scala:5: error: type Pair takes two type parameters, expected: one +object mp extends Monad[Pair] + ^ +one error found diff --git a/test/files/neg/wellkinded_wrongarity.scala b/test/files/neg/wellkinded_wrongarity.scala new file mode 100644 index 0000000000..39f8b7a2c3 --- /dev/null +++ b/test/files/neg/wellkinded_wrongarity.scala @@ -0,0 +1,5 @@ +// test well-kindedness checks -- arity error + +class Monad[m[x]] + +object mp extends Monad[Pair] \ No newline at end of file diff --git a/test/files/neg/wellkinded_wrongarity2.check b/test/files/neg/wellkinded_wrongarity2.check new file mode 100644 index 0000000000..353d2368a1 --- /dev/null +++ b/test/files/neg/wellkinded_wrongarity2.check @@ -0,0 +1,4 @@ +wellkinded_wrongarity2.scala:4: error: type String takes no type parameters, expected: one +object ms extends Monad[String] + ^ +one error found diff --git a/test/files/neg/wellkinded_wrongarity2.scala b/test/files/neg/wellkinded_wrongarity2.scala new file mode 100644 index 0000000000..ee9d771aaa --- /dev/null +++ b/test/files/neg/wellkinded_wrongarity2.scala @@ -0,0 +1,4 @@ +// test well-kindedness checks +class Monad[m[x]] + +object ms extends Monad[String] \ No newline at end of file diff --git a/test/files/pos/ksbug1.scala b/test/files/pos/ksbug1.scala new file mode 100644 index 0000000000..b3e1631370 --- /dev/null +++ b/test/files/pos/ksbug1.scala @@ -0,0 +1,4 @@ +object test { + type z[a,b] = a => b + def f : z[int,int] = (i => i + 1) +} \ No newline at end of file diff --git a/test/files/pos/nullary.scala b/test/files/pos/nullary.scala new file mode 100644 index 0000000000..22318d5143 --- /dev/null +++ b/test/files/pos/nullary.scala @@ -0,0 +1,20 @@ +abstract class NullaryTest[T, m[s]] { + def nullary: String = "a" + val x = nullary + + def nullary2: T + val x2 = nullary2 + + def nullary3: m[T] + val x3 = nullary3 +} + +class Concrete extends NullaryTest[Int, List] { + def nullary2 = 1 + def nullary3 = List(1,2,3) +} + +object test { + (new Concrete).nullary2 + (new Concrete).nullary3 +} \ No newline at end of file diff --git a/test/files/pos/nullary_poly.scala b/test/files/pos/nullary_poly.scala new file mode 100644 index 0000000000..9e3f239b36 --- /dev/null +++ b/test/files/pos/nullary_poly.scala @@ -0,0 +1,10 @@ +// test polymorphic nullary method calls +class A { + // built-in + synchronized {} + + val x: String = "a".asInstanceOf[String] + + // user-defined: + def polyNullary[T]: List[T] = Nil +} \ No newline at end of file diff --git a/test/files/pos/return_thistype.scala b/test/files/pos/return_thistype.scala new file mode 100644 index 0000000000..c0736c0ad9 --- /dev/null +++ b/test/files/pos/return_thistype.scala @@ -0,0 +1,8 @@ +// tests transformation of return type in typedTypeApply (see also tcpoly_gm.scala) +class As { + class A { + def foo: A.this.type = bar.asInstanceOf[A.this.type] + def foo2: this.type = bar.asInstanceOf[this.type] + def bar: A = null + } +} diff --git a/test/files/pos/tcpoly_boundedmonad.scala b/test/files/pos/tcpoly_boundedmonad.scala new file mode 100644 index 0000000000..ea19ae4c39 --- /dev/null +++ b/test/files/pos/tcpoly_boundedmonad.scala @@ -0,0 +1,19 @@ +trait Monad[T <: Bound[T], MyType[x <: Bound[x]], Bound[_]] { + def map[S <: Bound[S]](f: T => S): MyType[S] + + def flatMap[S <: RBound[S], RContainer[x <: RBound[x]], RBound[_], + Result[x <: RBound[x]] <: Monad[x, RContainer, RBound]] + (f: T => Result[S]): Result[S] + + def filter(p: T => Boolean): MyType[T] +} + +class Set[T <: Ordered[T]] extends Monad[T, Set, Ordered] { + def map[S <: Ordered[S]](f: T => S): Set[S] = error("TODO") + + def flatMap[S <: RBound[S], RContainer[x <: RBound[x]], RBound[_], + Result[x <: RBound[x]] <: Monad[x, RContainer, RBound]] + (f: T => Result[S]): Result[S] = error("TODO") + + def filter(p: T => Boolean): Set[T] = error("TODO") +} \ No newline at end of file diff --git a/test/files/pos/tcpoly_bounds1.scala b/test/files/pos/tcpoly_bounds1.scala new file mode 100644 index 0000000000..d74d3c975b --- /dev/null +++ b/test/files/pos/tcpoly_bounds1.scala @@ -0,0 +1,14 @@ +class Foo[t[x]<: Pair[Int, x]] + +// +class MyPair[z](a: Int, b: z) extends Pair[Int, z](a,b) + +object foo extends Foo[MyPair] + + +trait Monad[m[x <: Bound[x]], Bound[x], a] // TODO: variances! +trait ListMonad[a] extends Monad[List, Any, a] + +trait MyOrdered[a] +trait MySet[x <: MyOrdered[x]] +trait SetMonad[a <: MyOrdered[a]] extends Monad[MySet, MyOrdered, a] \ No newline at end of file diff --git a/test/files/pos/tcpoly_gm.scala b/test/files/pos/tcpoly_gm.scala new file mode 100644 index 0000000000..89b66cfba6 --- /dev/null +++ b/test/files/pos/tcpoly_gm.scala @@ -0,0 +1,14 @@ +trait Rep[a] { + def rep[m[x]]: m[a] // typedTypeApply must use asSeenFrom to adapt the return type + // since rep is called on x: Rep[t] + // a must become t +} + +case class ShowBin[b](app: b => String) + +object foo { + def showBin[t](x: Rep[t], y: t): String = { + val r: ShowBin[t] = x.rep[ShowBin] + r.app(y) + } +} diff --git a/test/files/pos/tcpoly_higherorder_bound_method.scala b/test/files/pos/tcpoly_higherorder_bound_method.scala new file mode 100644 index 0000000000..1e5cbf8092 --- /dev/null +++ b/test/files/pos/tcpoly_higherorder_bound_method.scala @@ -0,0 +1,3 @@ +trait SkolemisationOfHigherOrderBoundInMethod { + def method[A, N[X <: A], M[X <: N[A]]]: unit +} \ No newline at end of file diff --git a/test/files/pos/tcpoly_late_method_params.scala b/test/files/pos/tcpoly_late_method_params.scala new file mode 100644 index 0000000000..2af0664618 --- /dev/null +++ b/test/files/pos/tcpoly_late_method_params.scala @@ -0,0 +1,5 @@ +trait Foo { + def flatMap[RT <: RBound[RT], RBound[_], Result[x <: RBound[x]]]: Result[RT] +// bounds for RT& = >: scala.this.Nothing <: RBound&[RT&] + // bounds for x = >: scala.this.Nothing <: RBound&[x] +} \ No newline at end of file diff --git a/test/files/pos/tcpoly_method.scala b/test/files/pos/tcpoly_method.scala new file mode 100644 index 0000000000..34dde904c3 --- /dev/null +++ b/test/files/pos/tcpoly_method.scala @@ -0,0 +1,6 @@ +trait Iterable[m[+x], +t] { + def flatMap[resColl[+x] <: Iterable[resColl, x], s](f: t => resColl[s]): resColl[s] + + def foo[a[x]] = "a" + val x = foo[List] +} \ No newline at end of file diff --git a/test/files/pos/tcpoly_overloaded.scala b/test/files/pos/tcpoly_overloaded.scala new file mode 100644 index 0000000000..47cb68a8bb --- /dev/null +++ b/test/files/pos/tcpoly_overloaded.scala @@ -0,0 +1,25 @@ +trait Monad[T <: Bound[T], MyType[x <: Bound[x]], Bound[_]] { + def flatMap[S <: RBound[S], RContainer[x <: RBound[x]], RBound[_], + Result[x <: RBound[x]] <: Monad[x, RContainer, RBound]] + (f: T => Result[S]): Result[S] + def flatMap[S <: RBound[S], RContainer[x <: RBound[x]], RBound[_], + Result[x <: RBound[x]] <: Monad[x, RContainer, RBound]] + (f: T => Result[S], foo: String): Result[S] + def flatMap[S <: Bound[S]] + (f: T => MyType[S], foo: Int): MyType[S] +} + +trait Test { + def moo: MList[Int] + class MList[T](el: T) extends Monad[T, List, Any] { + def flatMap[S <: RBound[S], RContainer[x <: RBound[x]], RBound[_], + Result[x <: RBound[x]] <: Monad[x, RContainer, RBound]] + (f: T => Result[S]): Result[S] = error("foo") + def flatMap[S <: RBound[S], RContainer[x <: RBound[x]], RBound[_], + Result[x <: RBound[x]] <: Monad[x, RContainer, RBound]] + (f: T => Result[S], foo: String): Result[S] = error("foo") + def flatMap[S] + (f: T => List[S], foo: Int): List[S] = error("foo") + } + val l: MList[String] = moo.flatMap[String, List, Any, MList]((x: Int) => new MList("String")) +} \ No newline at end of file diff --git a/test/files/pos/tcpoly_param_scoping.scala b/test/files/pos/tcpoly_param_scoping.scala new file mode 100644 index 0000000000..1019fdfaf2 --- /dev/null +++ b/test/files/pos/tcpoly_param_scoping.scala @@ -0,0 +1,8 @@ +trait FOO[B, m[A <: B]] +trait FOO2[A <: B, B] +trait FOO3[m[A <: B], B] + +class Test { + def foo[a[x]] = "a" +} +//trait Idiom[idi[x]] { def foo: idi[Int]} \ No newline at end of file diff --git a/test/files/pos/tcpoly_poly.scala b/test/files/pos/tcpoly_poly.scala new file mode 100644 index 0000000000..1ba04e29df --- /dev/null +++ b/test/files/pos/tcpoly_poly.scala @@ -0,0 +1,3 @@ +class Monad[m[x]] + +object ml extends Monad[List] diff --git a/test/files/pos/tcpoly_return_overriding.scala b/test/files/pos/tcpoly_return_overriding.scala new file mode 100644 index 0000000000..cc7dd55cec --- /dev/null +++ b/test/files/pos/tcpoly_return_overriding.scala @@ -0,0 +1,13 @@ +trait Generic[g[x]] { + def unit: g[Unit] +} + +trait Rep[t] { + def rep[m[x]](implicit gen: Generic[m]): m[t] +} + +// testing that the return type is also transformed when checking overriding +// + that substitution (of types&symbols) preserves isHigherKinded when replacing a higher-kinded type with another one +object foo extends Rep[Unit] { + def rep[g[x]](implicit gen: Generic[g]): g[Unit]= gen.unit +} \ No newline at end of file diff --git a/test/files/pos/tcpoly_seq.scala b/test/files/pos/tcpoly_seq.scala new file mode 100644 index 0000000000..85563488fd --- /dev/null +++ b/test/files/pos/tcpoly_seq.scala @@ -0,0 +1,175 @@ +package examples.tcpoly.collection; + +trait HOSeq { + // an internal interface that encapsulates the accumulation of elements (of type elT) to produce + // a structure of type coll[elT] -- different kinds of collections should provide different implicit + // values implementing this interface, in order to provide more performant ways of building that structure + trait Accumulator[+coll[x], elT] { + def += (el: elT): Unit + def result: coll[elT] + } + + + // Iterable abstracts over the type of its structure as well as its elements (see PolyP's Bifunctor) + // m[x] is intentionally unbounded: fold can then be defined nicely + // variance: if we write m[+x] instead of +m[+x], x is an invariant position because its enclosing type + // is an invariant position -- should probably rule that out? + trait Iterable[+m[+x], +t] { + //def unit[a](orig: a): m[a] + def elements: Iterator[t] + + // construct an empty accumulator that will produce the same structure as this iterable, with elements of type t + def accumulator[t]: Accumulator[m, t] + + def filter(p: t => Boolean): m[t] = { + val buf = accumulator[t] + val elems = elements + while (elems.hasNext) { val x = elems.next; if (p(x)) buf += x } + buf.result + } + + def map[s](f: t => s): m[s] = { + val buf = accumulator[s] + val elems = elements + while (elems.hasNext) buf += f(elems.next) + buf.result + } + + // flatMap is a more specialized map, it only works if the mapped function produces Iterable values, + // which are then added to the result one by one + // the compiler should be able to find the right accumulator (implicit buf) to build the result + // to get concat, resColl = SingletonIterable, f = unit for SingletonIterable + def flatMap[resColl[+x] <: Iterable[resColl, x], s](f: t => resColl[s])(implicit buf: Accumulator[resColl, s]): resColl[s] = { + // TODO: would a viewbound for resColl[x] be better? + // -- 2nd-order type params are not yet in scope in view bound + val elems = elements + while (elems.hasNext) { + val elemss: Iterator[s] = f(elems.next).elements + while (elemss.hasNext) buf += elemss.next + } + buf.result + } + } + + final class ListBuffer[A] { + private var start: List[A] = Nil + private var last: ::[A] = _ + private var exported: boolean = false + + /** Appends a single element to this buffer. + * + * @param x the element to append. + */ + def += (x: A): unit = { + if (exported) copy + if (start.isEmpty) { + last = new HOSeq.this.:: (x, Nil) + start = last + } else { + val last1 = last + last = new HOSeq.this.:: (x, null) // hack: ::'s tail will actually be last + //last1.tl = last + } + } + + /** Converts this buffer to a list + */ + def toList: List[A] = { + exported = !start.isEmpty + start + } + + /** Clears the buffer contents. + */ + def clear: unit = { + start = Nil + exported = false + } + + /** Copy contents of this buffer */ + private def copy = { + var cursor = start + val limit = last.tail + clear + while (cursor ne limit) { + this += cursor.head + cursor = cursor.tail + } + } + } + + implicit def listAccumulator[elT]: Accumulator[List, elT] = new Accumulator[List, elT] { + private[this] val buff = new ListBuffer[elT] + def += (el: elT): Unit = buff += el + def result: List[elT] = buff.toList + } + + trait List[+t] extends Iterable[List, t] { + def head: t + def tail: List[t] + def isEmpty: Boolean + def elements: Iterator[t] = new Iterator[t] { + var these = List.this + def hasNext: Boolean = !these.isEmpty + def next: t = + if (!hasNext) + throw new NoSuchElementException("next on empty Iterator") + else { + val result = these.head; these = these.tail; result + } + } + // construct an empty accumulator that will produce the same structure as this iterable, with elements of type t + def accumulator[t]: Accumulator[List, t] = listAccumulator[t] + } + + // TODO: the var tl approach does not seem to work because subtyping isn't fully working yet + final case class ::[+b](hd: b, private val tl: List[b]) extends List[b] { + def head = hd + def tail = if(tl==null) this else tl // hack + override def isEmpty: boolean = false + } + + case object Nil extends List[Nothing] { + def isEmpty = true + def head: Nothing = + throw new NoSuchElementException("head of empty list") + def tail: List[Nothing] = + throw new NoSuchElementException("tail of empty list") + } +} + + + +// misc signatures collected from mailing list / library code: + /*override def flatMap[B](f: A => Iterable[B]): Set[B] + final override def flatMap[b](f: Any => Iterable[b]): Array[b] + def flatMap[b](f: a => Parser[b]) = new Parser[b] + override def flatMap[b](f: a => Iterable[b]): List[b] + + + MapResult[K] <: Seq[K] + FilterResult <: Seq[T] + Concat <: Seq[T] + Subseq <: Seq[T] + + + def map[K](f: T=>K): MapResult[K] + def filter(f: T=>Boolean): FilterResult + def subseq(from: int, to: int): Subseq + def flatMap[S <: Seq[K], K](f: T => S): S#Concat // legal? + def concat(others: Seq[T]): Concat + */ + +/*trait Iterator[t] { + // @post hasAdvanced implies hasNext + // model def hasAdvanced: Boolean + + def hasNext: Boolean // pure + + // @pre hasAdvanced + def current: t // pure + + // @pre hasNext + // @post hasAdvanced + def advance: Unit +}*/ \ No newline at end of file diff --git a/test/files/pos/tcpoly_seq_typealias.scala b/test/files/pos/tcpoly_seq_typealias.scala new file mode 100644 index 0000000000..0498c3b2f6 --- /dev/null +++ b/test/files/pos/tcpoly_seq_typealias.scala @@ -0,0 +1,143 @@ +package examples.tcpoly.collection; + +trait HOSeq { + // an internal interface that encapsulates the accumulation of elements (of type elT) to produce + // a structure of type coll[elT] -- different kinds of collections should provide different implicit + // values implementing this interface, in order to provide more performant ways of building that structure + trait Accumulator[+coll[x], elT] { + def += (el: elT): Unit + def result: coll[elT] + } + + + // Iterable abstracts over the type of its structure as well as its elements (see PolyP's Bifunctor) + // m[x] is intentionally unbounded: fold can then be defined nicely + // variance: if we write m[+x] instead of +m[+x], x is an invariant position because its enclosing type + // is an invariant position -- should probably rule that out? + trait Iterable[+t] { + type m[+x] + + //def unit[a](orig: a): m[a] + def elements: Iterator[t] + + // construct an empty accumulator that will produce the same structure as this iterable, with elements of type t + def accumulator[t]: Accumulator[m, t] + + def filter(p: t => Boolean): m[t] = { + val buf = accumulator[t] + val elems = elements + while (elems.hasNext) { val x = elems.next; if (p(x)) buf += x } + buf.result + } + + def map[s](f: t => s): m[s] = { + val buf = accumulator[s] + val elems = elements + while (elems.hasNext) buf += f(elems.next) + buf.result + } + + // flatMap is a more specialized map, it only works if the mapped function produces Iterable values, + // which are then added to the result one by one + // the compiler should be able to find the right accumulator (implicit buf) to build the result + // to get concat, resColl = SingletonIterable, f = unit for SingletonIterable + def flatMap[resColl[+x] <: Iterable[x], s](f: t => resColl[s])(implicit buf: Accumulator[resColl, s]): resColl[s] = { + // TODO: would a viewbound for resColl[x] be better? + // -- 2nd-order type params are not yet in scope in view bound + val elems = elements + while (elems.hasNext) { + val elemss: Iterator[s] = f(elems.next).elements + while (elemss.hasNext) buf += elemss.next + } + buf.result + } + } + + final class ListBuffer[A] { + private var start: List[A] = Nil + private var last: ::[A] = _ + private var exported: boolean = false + + /** Appends a single element to this buffer. + * + * @param x the element to append. + */ + def += (x: A): unit = { + if (exported) copy + if (start.isEmpty) { + last = new HOSeq.this.:: (x, Nil) + start = last + } else { + val last1 = last + last = new HOSeq.this.:: (x, null) // hack: ::'s tail will actually be last + //last1.tl = last + } + } + + /** Converts this buffer to a list + */ + def toList: List[A] = { + exported = !start.isEmpty + start + } + + /** Clears the buffer contents. + */ + def clear: unit = { + start = Nil + exported = false + } + + /** Copy contents of this buffer */ + private def copy = { + var cursor = start + val limit = last.tail + clear + while (cursor ne limit) { + this += cursor.head + cursor = cursor.tail + } + } + } + + implicit def listAccumulator[elT]: Accumulator[List, elT] = new Accumulator[List, elT] { + private[this] val buff = new ListBuffer[elT] + def += (el: elT): Unit = buff += el + def result: List[elT] = buff.toList + } + + trait List[+t] extends Iterable[t] { + type m[+x] = List[x] + + def head: t + def tail: List[t] + def isEmpty: Boolean + def elements: Iterator[t] = new Iterator[t] { + var these = List.this + def hasNext: Boolean = !these.isEmpty + def next: t = + if (!hasNext) + throw new NoSuchElementException("next on empty Iterator") + else { + val result = these.head; these = these.tail; result + } + } + // construct an empty accumulator that will produce the same structure as this iterable, with elements of type t + def accumulator[t]: Accumulator[List, t] = listAccumulator[t] + } + + // TODO: the var tl approach does not seem to work because subtyping isn't fully working yet + final case class ::[+b](hd: b, private val tl: List[b]) extends List[b] { + def head = hd + def tail = if(tl==null) this else tl // hack + override def isEmpty: boolean = false + } + + case object Nil extends List[Nothing] { + def isEmpty = true + def head: Nothing = + throw new NoSuchElementException("head of empty list") + def tail: List[Nothing] = + throw new NoSuchElementException("tail of empty list") + } +} \ No newline at end of file diff --git a/test/files/pos/tcpoly_subst.scala b/test/files/pos/tcpoly_subst.scala new file mode 100644 index 0000000000..af474585e9 --- /dev/null +++ b/test/files/pos/tcpoly_subst.scala @@ -0,0 +1,4 @@ +object test { + def make[m[x], b]: m[b] = error("foo") + val lst: List[Int] = make[List, Int] +} \ No newline at end of file diff --git a/test/files/pos/tcpoly_typesub.scala b/test/files/pos/tcpoly_typesub.scala new file mode 100644 index 0000000000..bb29d88bd9 --- /dev/null +++ b/test/files/pos/tcpoly_typesub.scala @@ -0,0 +1,14 @@ +// contributed by Lauri Alanko +trait TypeSub { + type l + type u + def castSub[f[+x]](fl : f[l]) : f[u] + def castSuper[f[-x]](fu : f[u]) : f[l] = { + type c[+y] = f[y] => f[l] + castSub[c]{ fl : f[l] => fl }(fu) + } + def castValue[t](lt : l with t) : u with t = { + type c[+y] = y with t + castSub[c](lt) + } +} \ No newline at end of file diff --git a/test/files/pos/tcpoly_variance.scala b/test/files/pos/tcpoly_variance.scala new file mode 100644 index 0000000000..9e034ef97e --- /dev/null +++ b/test/files/pos/tcpoly_variance.scala @@ -0,0 +1,7 @@ +class A[m[+x]] { + def str: m[Object] = error("foo") +} + +class B[m[+x]] extends A[m] { + override def str: m[String] = error("foo") +} \ No newline at end of file diff --git a/test/files/pos/tcpoly_wildcards.scala b/test/files/pos/tcpoly_wildcards.scala new file mode 100644 index 0000000000..eed0e6f685 --- /dev/null +++ b/test/files/pos/tcpoly_wildcards.scala @@ -0,0 +1,3 @@ +trait test[b[_,_]] { + def moo[a[_, _]] = error("a") +} \ No newline at end of file diff --git a/test/files/pos/typealias_dubious.scala b/test/files/pos/typealias_dubious.scala new file mode 100644 index 0000000000..98df5c1290 --- /dev/null +++ b/test/files/pos/typealias_dubious.scala @@ -0,0 +1,15 @@ +class MailBox { + //class Message + type Message = AnyRef +} + +abstract class Actor { + private val in = new MailBox + + def send(msg: in.Message) = error("foo") + + def unstable: Actor = error("foo") + + def dubiousSend(msg: MailBox#Message) = + unstable.send(msg) // in.Message becomes unstable.Message, but that's ok since Message is a concrete type member +} \ No newline at end of file diff --git a/test/files/pos/typealiases.scala b/test/files/pos/typealiases.scala new file mode 100644 index 0000000000..c32a3e3a78 --- /dev/null +++ b/test/files/pos/typealiases.scala @@ -0,0 +1,20 @@ +package foo + +trait Test[T] { + type Check[T] = Array[T] => Unit; + type MyPair[S] = Pair[T, S] + + val pair1: Pair[T, Int] + val pair: MyPair[Int] = pair1 + + def check(xs: Array[T], c: Check[T]) = c(xs) + def check2[S](xs: Array[S], c: Check[S]) = c(xs) +} + +object main extends Test[Int] { + val pair1 = (1,1) + + implicit def topair(x: Int): Pair[Int, Int] = (x,x) + val pair2: MyPair[Int] = 1 + val x: short = 1 +} \ No newline at end of file diff --git a/test/files/run/tcpoly_monads.check b/test/files/run/tcpoly_monads.check new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/test/files/run/tcpoly_monads.check @@ -0,0 +1 @@ +42 diff --git a/test/files/run/tcpoly_monads.scala b/test/files/run/tcpoly_monads.scala new file mode 100644 index 0000000000..0ee160803a --- /dev/null +++ b/test/files/run/tcpoly_monads.scala @@ -0,0 +1,42 @@ +trait Monads { + /** + * class Monad m where + * (>>=) :: m a -> (a -> m b) -> m b + * return :: a -> m a + * + * MonadTC encodes the above Haskell type class, + * an instance of MonadTC corresponds to a method dictionary. + * (see http://lampwww.epfl.ch/~odersky/talks/wg2.8-boston06.pdf) + * + * Note that the identity (`this') of the method dictionary does not really correspond + * to the instance of m[x] (`self') that is `wrapped': e.g., unit does not use `self' (which + * corresponds to the argument of the implicit conversion that encodes an instance of this type class) + */ + trait MonadTC[m[x], a] { + def unit[a](orig: a): m[a] + + // >>='s first argument comes from the implicit definition constructing this "method dictionary" + def >>=[b](fun: a => m[b]): m[b] + } +} + +/** + * instance Monad Maybe where + * (Just x) >>= k = k x + * Nothing >>= _ = Nothing + */ +trait OptionMonad extends Monads { + // this implicit method encodes the Monad type class instance for Option + implicit def OptionInstOfMonad[a](self: Option[a]): MonadTC[Option, a] + = new MonadTC[Option, a] { + def unit[a](orig: a) = Some(orig) + def >>=[b](fun: a => Option[b]): Option[b] = self match { + case Some(x) => fun(x) + case None => None + } + } +} + +object Test extends OptionMonad with Application { + Console.println((Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") >>= (x => Some(x.length))).get) +} \ No newline at end of file diff --git a/test/files/run/tcpoly_overriding.check b/test/files/run/tcpoly_overriding.check new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/test/files/run/tcpoly_overriding.check @@ -0,0 +1 @@ +1 diff --git a/test/files/run/tcpoly_overriding.scala b/test/files/run/tcpoly_overriding.scala new file mode 100644 index 0000000000..970e16c71f --- /dev/null +++ b/test/files/run/tcpoly_overriding.scala @@ -0,0 +1,16 @@ +abstract class A[t[x]] { + def b: t[Int] +} + +class B extends A[List] { + // underlying functionality being tested is overriding, but bugs manifest itself during erasure + // erasure should generate two methods: one that returns an Object (to implement the method in A) + // one that is as close as possible to the original method and thus returns a List + // the problem only manifests itself here -- but it's really a problem with overriding + // the link between this method and the method in A isn't seen + def b: List[Int] = List(1) +} + +object Test extends Application { + Console.println((new B).b(0)) +} \ No newline at end of file diff --git a/test/files/run/tcpoly_parseridioms.check b/test/files/run/tcpoly_parseridioms.check new file mode 100644 index 0000000000..5fff2efb15 --- /dev/null +++ b/test/files/run/tcpoly_parseridioms.check @@ -0,0 +1 @@ +Success(List(),Plus(1,2)) diff --git a/test/files/run/tcpoly_parseridioms.scala b/test/files/run/tcpoly_parseridioms.scala new file mode 100644 index 0000000000..e163ea2ce8 --- /dev/null +++ b/test/files/run/tcpoly_parseridioms.scala @@ -0,0 +1,109 @@ +trait Parsers { + type Input=List[char] + + sealed class ParseResult[+t](val next: Input) + case class Success[+t](override val next: Input, result: t) extends ParseResult[t](next) + case class Failure(override val next: Input, msg: String) extends ParseResult[Nothing](next) + + abstract class Parser[+t] { + def apply(in: Input): ParseResult[t] + } + + // sequence + def sq[T, U](a: => Parser[T], b: => Parser[U]): Parser[Pair[T, U]] = new Parser[Pair[T, U]] { + def apply(in: Input): ParseResult[Pair[T, U]] = a(in) match { + case Success(next, x) => b(next) match { + case Success(next2, y) => Success(next2, Pair(x,y)) + case Failure(_, msg) => Failure(in, msg) + } + case Failure(_, msg) => Failure(in, msg) + } + } + + // ordered choice + def or[T, U <: T](a: => Parser[T], b: => Parser[U]): Parser[T] = new Parser[T] { + def apply(in: Input): ParseResult[T] = a(in) match { + case Success(next, x) => Success(next, x) + case Failure(_, _) => b(in) match { + case Success(next, y) => Success(next, y) + case Failure(_, msg) => Failure(in, msg) + } + } + } + + // lifting + def lift[T, U](f: T => U)(a: => Parser[T]): Parser[U] = new Parser[U] { + def apply(in: Input): ParseResult[U] = a(in) match { + case Success(n, x) => Success(n, f(x)) + case Failure(n, msg) => Failure(n, msg) + } + } + + def accept[T](c: Char, r: T): Parser[T] = new Parser[T] { + def apply(in: Input) = in match { + case c2 :: n if c2 == c => Success(n, r) + case n => Failure(n, "expected "+c+" at the head of "+n) + } + } + + def apply_++[s, tt](fun: Parser[s => tt], arg: Parser[s]): Parser[tt] = lift[Pair[s=>tt, s], tt]({case Pair(f, a) => f(a)})(sq(fun, arg)) + + def success[u](v: u): Parser[u] = new Parser[u] { + def apply(in: Input) = Success(in, v) + } + +} + +trait Idioms { + trait Idiom[idi[x]] { + def liftedApply[s, t](fun: idi[s => t])(arg: idi[s]): idi[t] + def pure[a](x: a): idi[a] + def pureMethod[a](name: String, x: a): idi[a] = pure(x) // hack for Mirrors: allow passing of method names + } + + class IdiomaticTarget[idi[x], idiom <: Idiom[idi], s](i: idiom, tgt: s) { + def dot [t](fun: s => t, name: String) = new IdiomaticApp2[idi, idiom, t](i, i.liftedApply(i.pureMethod(name, fun))(i.pure(tgt))) + } // TODO: `.` --> java.lang.ClassFormatError: Illegal method name "." in class Idioms$IdiomaticTarget + + class IdiomaticFunction[idi[x], idiom <: Idiom[idi], s, t](i: idiom, fun: s => t) { + def `(` (a: idi[s]) = new IdiomaticApp[idi, idiom, t](i, i.liftedApply(i.pure(fun))(a)) + } + + class IdiomaticApp[idi[x], idiom <: Idiom[idi], x](i: idiom, a: idi[x]) { + // where x <: s=>t -- TODO can this be expressed without generalised constraints? + def `,` [s, t](b: idi[s]) = new IdiomaticApp[idi, idiom, t](i, i.liftedApply(a.asInstanceOf[idi[s=>t]])(b)) + + def `)` : idi[x] = a + } + + class IdiomaticApp2[idi[x], idiom <: Idiom[idi], x](i: idiom, a: idi[x]) extends IdiomaticApp[idi, idiom, x](i, a) { + def `(` [s, t](b: idi[s]) = `,`[s,t](b) + } +} + +trait ParserIdioms extends Parsers with Idioms { + object ParserIdiom extends Idiom[Parser] { + def liftedApply[s, t](fun: Parser[s => t])(arg: Parser[s]): Parser[t] = apply_++(fun, arg) + def pure[a](x: a): Parser[a] = success(x) + } + + implicit def parserIdiomFun[s, t](fun: s=>t): IdiomaticFunction[Parser, ParserIdiom.type, s, t] = + new IdiomaticFunction[Parser, ParserIdiom.type, s, t](ParserIdiom, fun) + implicit def parserIdiomTgt[s](tgt: s): IdiomaticTarget[Parser, ParserIdiom.type, s] = + new IdiomaticTarget[Parser, ParserIdiom.type, s](ParserIdiom, tgt) + + trait Expr + case class Plus(a: Int, b: Int) extends Expr + + def num = or(accept('0', 0), or(accept('1', 1),accept('2', 2))) + + // TODO: how can parserIdiom(curry2(_)) be omitted? + def expr: Parser[Expr] = parserIdiomFun(curry2(Plus)) `(` num `,` num `)` + + implicit def curry2[s,t,u](fun: (s, t)=>u)(a: s)(b: t) = fun(a, b) + implicit def curry3[r,s,t,u](fun: (r,s, t)=>u)(a: r)(b: s)(c: t) = fun(a, b, c) +} + +object Test extends ParserIdioms with Application { + Console.println(expr(List.fromString("12"))) +} -- cgit v1.2.3