Closed scabug closed 13 years ago
Imported From: https://issues.scala-lang.org/browse/SI-4079?orig=1 Reporter: @pchiusano Attachments:
@pchiusano said: Compile fine alone
@pchiusano said: Bug.scala compiled as a jar
@pchiusano said: Bombs during compilation when linked against the bug jar
@pchiusano said: I found what looks to be a workaround, using [http://groups.google.com/group/scalaz/browse_thread/thread/862543e872b09f3c type lambdas] instead of the ComposeT trait. The working file looks like:
trait Functor[F[_]] {
def map[A,B](fa: F[A], f: A => B): F[B]
}
case class Compose[F[_],G[_]]() {
def Functor(implicit f: Functor[F], g: Functor[G]): Functor[({type f[x] = F[G[x]]})#f] =
new Functor[({type f[x] = F[G[x]]})#f] {
def map[A,B](c: F[G[A]], h: A => B): F[G[B]] =
f.map(c, (x:G[A]) => g.map(x,h))
}
}
object Cat {
def compose[F[_],G[_]] = Compose[F,G]()
}
object Functors {
implicit val List = new Functor[List] {
def map[A,B](fa: List[A], f: A => B): List[B] = fa map f
}
implicit val Option = new Functor[Option] {
def map[A,B](fa: Option[A], f: A => B): Option[B] = fa map f
}
}
object Main {
import Functors._
val cf = Cat.compose[List,Option].Functor
}
@paulp said: I don't know what I'm doing, but poking around at things is sort of a hobby of mine, so:
scala> import Functors._
import Functors._
scala> Cat.compose[List,Option].Functor.map(List(Some(1), Some(2)), (x: Int) => x + 2)
res0: List[Option[Int]] = List(Some(3), Some(4))
Here's the diff against trunk which made the bad men go away (in this example anyway, I make no claims of generality.)
diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala
index 9967d07..9bf9ff9 100644
--- a/src/compiler/scala/tools/nsc/symtab/Types.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Types.scala
@@ -3358,7 +3358,8 @@ A type's typeSymbol should never be inspected directly.
def throwError = abort("" + tp + sym.locationString + " cannot be instantiated from " + pre.widen)
def instParam(ps: List[Symbol], as: List[Type]): Type =
- if (ps.isEmpty) throwError
+ if (ps.isEmpty && as.isEmpty) mapOver(tp)
+ else if (ps.isEmpty) throwError
else if (sym eq ps.head)
// @M! don't just replace the whole thing, might be followed by type application
appliedType(as.head, args mapConserve (this)) // @M: was as.head
@paulp said: It makes the bad men go away in #2741 as well, which makes it pretty safe this is a duplicate of that.
@hubertp said: Looks like a duplicate to me as well, but I will let Adriaan to make a final tick.
@pchiusano said: Paul - awesome.
@adriaanm said: i don't want to be the bad guy, but this is a symptomatic fix
the deeper problem, at least for #2741, and most like here as well, is related to a lacking implementation of anonymous type functions I now how to fix it, but it's a big change.
asSeenFrom shouldn't even be trying to instantiate A in Compose, as it defines no such type parameter
@paulp said: Replying to [comment:6 moors]:
i don't want to be the bad guy, but this is a symptomatic fix
You're not the bad guy - I never doubted it.
@paulp said: And I think it's nice that we have a ticket where both bad men and bad guys make appearances. Be on the lookout for Black Bart and his gang of higher kinded existentials!
@paulp said: Replying to [comment:6 moors]:
i don't want to be the bad guy, but this is a symptomatic fix
But an addendum: now having verified that the symptomatic fix does not break anything we have a test for, we shouldn't let its imperfection keep us from committing it: it's a single spectacularly well-localized line, rather a long ways from the worst hack in the compiler. This goes double if the proper fix is indeed heavy and code-touchy.
NOW we'll find out who's the bad guy!
@adriaanm said: (In r24029) introduce NullaryMethodType to disambiguate PolyType
motivation:
given def foo[T]: (T, T)
and type Foo[T] = (T, T)
,
foo.info
and TypeRef(_, Foo, Nil).normalize
are both PolyType(List(T), Pair[T, T])
uncurry has been relying on an ugly hack to distinguish these cases based on ad-hoc kind inference
without this distinction, the type alias's info (a type function) would be transformed to PolyType(List(T), MethodType(Nil, Pair[T, T]))
anonymous type functions are being used more often (see #2741, #4017, #4079, #3443, #3106), which makes a proper treatment of PolyTypes more pressing
change to type representation: PolyType(Nil, tp) -> NullaryMethodType(tp) PolyType(tps, tp) -> PolyType(tps, NullaryMethodType(tp)) (if the polytype denoted a polymorphic nullary method)
PolyType(Nil, tp) is now invalid
the kind of a PolyType is * iff its resulttype is a NullaryMethodType or a MethodType (i.e., it's a polymorphic value) in all other cases a PolyType now denotes a type constructor
NullaryMethodType is eliminated during uncurry
pickling: for backwards compatibility, a NullaryMethodType(tp) is still pickled as a PolyType(Nil, tp), unpickling rewrites pre-2.9-pickled PolyTypes according to the expected kind of the unpickled type (similar to what we used to do in uncurry) a pickled PolyType(Nil, restpe) is unpickled to NullaryMethodType(restpe) a pickled PolyType(tps, restpe) is unpickled to PolyType(tps, NullaryMethodType(restpe)) when the type is expected to have kind *
the rewrite probably isn't complete, but was validated by compiling against the old scalacheck jar (which has plenty of polymorphic nullary methods) nevertheless, this commit includes a new scalacheck jar
summary of the refactoring:
PolyType(List(), tp) or PolyType(Nil, tp) or PolyType(parms, tp) if params.isEmpty ==> NullaryMethodType(tp)
whenever there was a case PolyType(tps, tp) (irrespective of tps isEmpty), now need to consider the case PolyType(tps, NullaryMethodType(tp)); just add a case NullaryMethodType(tp), since usually:
tp.resultType, where tp was assumed to be a PolyType that represents a polymorphic nullary method type before, tp == PolyType(tps, res), now tp == PolyType(tps, NullaryMethodType(res))
got bitten again (last time was dependent-method types refactoring) by a TypeMap not being the identity when dropNonConstraintAnnotations is true (despite having an identity apply method). Since asSeenFrom is skipped when isTrivial, the annotations aren't dropped. The cps plugin relies on asSeenFrom dropping these annotations for trivial types though. Therefore, NullaryMethodType pretends to never be trivial. Better fix(?) in AsSeenFromMap: if(tp.isTrivial) dropNonContraintAnnotations(tp) else ...
TODO: scalap and eclipse
review by odersky, rytz
@adriaanm said: (In r24091) closes #2741, #4079: pickling now ensures that a local type param with a non-local owner, which will thus get a localized owner, will only get a class as its localized owner if its old owner was a class (otherwise, NoSymbol) this ensures that asSeenFrom does not treat typerefs to this symbol differently after pickling.
todo: should we pro-actively set the owner of these type params to something else than the type alias that they originate from? see notes in typeFunAnon
review by odersky
It's possible this is a duplicate of #2741. I've gotten down to a pretty simple test case though.
Compiling the following code seems to work fine:
If I then invoke this code from the interpreter (or a separate compilation unit), I get this stack trace:
What is odd is that if I put the call to Cat.compose to the same file, it doesn't seem to be a problem. The error only occurs when Cat.scala is compiled separately from the call to Cat.compose[F,G].Functor.
So, to reproduce, compile Bug.scala into a jar, bug.jar, then try compiling BugRef.scala, referencing bug.jar.
=== What versions of the following are you using? ===