Skip to content

Add implicit function types #1775

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
Dec 18, 2016
Merged

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Dec 5, 2016

This is the first step to bring contextual abstraction to Scala.

@@ -1128,8 +1128,18 @@ object Parsers {
val name = bindingName()
val t =
if (in.token == COLON && location == Location.InBlock) {
if (false) // Don't error yet, as the alternative syntax "implicit (x: T) => ... "
// is not supported by Scala2.x
migrationWarningOrError(s"This syntax is no longer supported; parameter needs to be enclosed in (...)")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need two levels of migration warnings: those that can be fixed in scala/dotty and those that can only be fixed (currently) in dotty, for example the fact that we currently warn on every usage of with is quite annoying.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.

Copy link
Contributor

@edmundnoble edmundnoble left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellently done. We'll beat Idris in no time. I have a few more questions:

  1. How does this interact with specialization of the FunctionN types for lower N?
  2. How does this interact with FunctionXXL?

- managing capabilities for security critical tasks,
- wiring components up with dependency injection,
- defining the meanings of operations with type classes,
- more generally, passing any sort of context to a computation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can only see a very weak relationship between comonads and implicit functions. At best, implicit functions can replace coreader comonad stacks. Other than that, comonads are completely irrelevant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant to refer to the original paper "Implicit parameters: dynamic scoping with static types" (POPL2000) which speculated that the best fitting foundation of implicit parameters are comonads. But thinking about it I run into troubles to nail it down.

Copy link
Contributor Author

@odersky odersky Dec 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I have worked it out now: implicit functions are both monads and comonads, with

M[A]        =  implicit X => A
unit a      =  implicit (_: X) => a
counit m    =  m (implicitly[X])
map m f     =  implicit (_: X) => f (m (implicitly[X]))
            =  unit (f (counit m))

join m      =  counit m 
duplicate m =  unit m

WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You aren't just using the implicit function there, you're also using the implicitly[X] value in counit. The comonad you present is the Store comonad (a, a -> b), and the monad is the reader monad. I believe coeffects, which are a calculus of dependencies (http://tomasp.net/academic/papers/structural/), might provide a better theoretical underpinning.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The monad is indeed close to the reader monad, but I fail to see that it's the Store comonad. Where is the tupling?

Copy link
Contributor Author

@odersky odersky Dec 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's something else at play here, and I am trying to wrap my head around it. If we drop the implicitness and just talk about function abstraction and application we also get a bimonad in some sense:

M[A]        =  X => A
unit a      =  (x: X) => a
counit m    =  m x
map m f     =  (x: X) => f (m x)
            =  unit (f (counit m))

join m      =  counit m 
duplicate m =  unit m

But this feels a little bit weirder because now we are dealing with open terms, with counit operations introducing free variables that are captured by unit operations. And the names of both free variables and lambda binders are fixed to be always the same name x.

By contrast, all terms in the implicit function bimonad representation are closed. I am not sure what difference it makes, though.

Copy link
Contributor Author

@odersky odersky Dec 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding composition, I believe one can explain the composability of implicit functions from the principle that they are a bimonad. Let's say you have two function types F and G, which take implicit parameters of types X and Y, but in different orders:

type F =  implicit X => implicit Y => A
type G =  implicit Y => implicit X => A

I can turn a value f: F into a value of type G like this:

implicit Y => implicit X => f(implicitly[X])(implicitly[Y])

Moreover that conversion is completely automatic. I just have to write

val x: G  = f

and the rest is produced automatically. I believe that gets us to the essence why implicit functions are better composable than general monads. Even if we disregard the aspect of implicit code insertion, what happens here is that we strip all (co)monads in the applications to the two implicitly arguments and then reapply the (co)monads in the outer lambda abstractions. The fact that this is a bimonad is essential here, because it means we can always get out of an entangled structure with counit operations, reorder at the outermost level, and get back with unit operations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that your insight that the regular function bimonad requires the ability to introduce terms into the environment shows that the implicit function comonad also requires that ability. But yes, I agree that being able to implicitly reorder implicit arguments decreases their syntactic overhead below reader.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @edmundnoble: in particular what you get with implicits isn't closed terms, because they assume something in the context. I haven't read the paper, but maybe they had some other category in mind? (Warning: dense and unchecked ideas ahead). Beyond the category of functions you're using, you could probably have (equivalent I think) categories of expressions with x in scope and of expressions with some implicit in scope, and there your comonad might work. I haven't checked any of the details though. Whether that's insightful or useful, beyond making precise the two things are equivalent, is not clear.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see a mention of my work on coeffects in the thread already, so I thought I'll add my perspective. I'm not all that familiar with Scala, so I'll stick to my Haskell/ML-style notation, but I hope that will be understandable.

What are coeffects
First of all, coeffects are, indeed, closely related to comonads (they are modelled using "indexed comonads", which is a generalisation of ordinary comonads). What coeffects add, is that they track more precisely what context is needed. The comonadic (or monadic) type C a or M a tells you that there is some context-requirement or some effect, but it does not tell you what precisely. Coeffects add an annotation so that you have types such as C {?p:int, ?q:int} a where the annotation {?p:int, ?q:int} says you need two implicit parameters ?p and ?q. So you can see coeffects as more precise comonads.

Are implicit parameters monads, comonads or coeffects
Implicit parameters are one of the motivating examples in the work on coeffects, but they are just one (coeffects capture other interesting contextual properties). The interesting thing about implicit parameters is that they can almost be modelled by both Reader monad and Product comonad. With monads, you use functions a -> M b for some monad and with comonads, you use functions C a -> b for some comonad.

With Reader monad, we have M a = State -> a and so:

a -> M b = a -> (State -> b)

With Product comonad, we have C a = a * State and so:

C a -> b = a * State -> b

Now you can see that the two functions, a * State -> b and a -> State -> b are related (via currying)! 🎉

There is one thing that reader monad does not let you do. It does not let you model the facts that implicit parameters (in GHC) support both lexical and dynamic scoping. Take for example this:

let ?x = 1
let f = (fun n ->?x + ?y)

If we model this using Reader monad and ?x and ?y mean "read implicit parameter from the monad", then the type of f will be a function that needs ?x and ?y. However, if we do this using comonads, the context available in the function body can combine context provided by the caller with the context provided by the declaration site - and so we can use ?x from the declaration site and end up with a function that needs just ?y (I'd be quite curious to see what Scala is doing here - are implicits just dynamically scoped or mix of both?

Links with more information
There is a lot more about this than I can fit in a comment, so for those interested:

- defining the meanings of operations with type classes,
- more generally, passing any sort of context to a computation.

Implicit function types are a suprisingly simple and general way to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/suprisingly/surprisingly

make coding patterns solving these tasks abstractable, reducing
boilerplate code and increasing applicability.

*First Step* My pull request is first implementation. In solves the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/first/the first

accesses. Monads don't compose in general, and therefore even simple
combinations need to be expressed on the level of monad transformers,
at the price of much boilerplate and complexity. Recognizing this,
peaple have recently experimented with free monads, which alleviate
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/peaple/people

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The free monad is almost entirely orthogonal to this issue. It solves the monad composition problem, but the reader monad can be trivially composed with itself even without it. I'd also be very interested in a citation for your statement as to inefficient use of the reader monad, because the reader monad is just a function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I need a citation. It's evident: With a monad every primitive step (a semicolon if you will) becomes a bind, which means a closure object is created and discarded. That's at least an order of magnitude in performance loss on the JVM. So I think the proof obligation is clearly to show that monadic code is somehow not as inefficient as it looks.

Copy link
Contributor Author

@odersky odersky Dec 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but the reader monad can be trivially composed with itself even without it.

But what about composing two reader monads that read each a different thing? Or composing a reader monad with a transaction monad, say? I don't see a general framework to do these things, even though one could think of ad-hoc solutions to compose some specific monads.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For your first point: monadic code and code dispatching on a monad typeclass instance are not equivalent. The concrete use of a monad in particular will likely result in all binds being inlined, seeing as scalac is so paranoid about inlining higher order functions.

Composing two different reader monads Reader[A] and Reader[B] is as simple as Reader[(A, B)]; as inefficient as tupling and projection. You are completely correct though in that there is no general framework to compose or combine monads (or rather there are quite a few but no clear choice). I suppose my comment is just that despite the fact that monads in general do not compose, the Reader monad does, so it's not an apples-to-apples comparison with implicit params, which do compose.

Copy link
Contributor Author

@odersky odersky Dec 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the performance I believe we will need concrete data. Knowing the compiler well, my strong belief is that monadic composition has terrible performance (in particular if you add trampolining, that's another order of magnitude slowdown). I believe the proof obligation is firmly in the other camp: that monadic code is somehow not so terrible after all as it appears.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree entirely that we need concrete data, but at the same time Readers are just functions. It's very easy to use function objects (and Readers) in a way which does a lot of unnecessary dispatch and allocation, and I agree that it's definitely worth investigating how the monadic API contributes to that. I have not seen any literature on Reader performance, only Writer and State, because they tend to do so incredibly badly. Also: yes, trampolining can shave an order of magnitude off your performance, but it's also a large hammer. Trampolining is a brute-force tool to make things stack-safe, and the FP community in Scala has noticed its terrible performance and started to move away from it where it can (especially in the case of monadic composition).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note @edmundnoble Haskell performance and Scala performance of the same construct are not related. Scala has a very minimal optimizer compared to GHC for various reasons. Also, Scalac does no automatic inlining IIRC, it's users that request inlining via annotations for higher-order functions.

capabilities, dictionaries, or whatever contextual data the functions
need. The only downside with this is that often there's a large
distance in the call graph between the definition of a contextual
element and the site where it is used. Conseuqently, it becomes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/Conseuqently/Consequently

pattern because it is lightweight and can express context changes in a
purely functional way.

The main downside of implicit parameters is the verbosity of their
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just editorializing: in my opinion, the main downside of implicit parameters is that they make code harder to refactor by making the place where your code exists change the semantics of the code. The verbosity is definitely an issue on its own, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But that's nothing new. All free bindings of a piece of code affect its semantics. So the semantics of any expression that's not closed depends on its location (?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point, free bindings impede refactoring whether they're implicit or not. My mistake.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But that's nothing new. All free bindings of a piece of code affect its semantics. So the semantics of any expression that's not closed depends on its location (?)

Implicits simply hide the free variables in use, so that figuring out the context requires typechecking rather than just parsing. So, after you look up implicitly's type, you discover that implicitly[Foo] is an open term (which is sugar for open term implicitly[Foo](theImplicitDefinition)). This is confusing enough it was (indirectly) discussed above: #1775 (comment)

@odersky
Copy link
Contributor Author

odersky commented Dec 7, 2016

In light of the (very interesting!) discussion about monads and comonads here I think it's better to talk about "contextual" abstraction, so I have changed that line in the PR explanation. It seems like comonadic comes into play but what implicit functions provide cannot be explained exclusively (or even primarily) by their comonadicness.

@liufengyun
Copy link
Contributor

I drafted the immature idea of dynamic parameters here, which is marginally related to this discussion.

@odersky odersky closed this Dec 7, 2016
@odersky odersky deleted the add-implicit-funtypes branch December 7, 2016 10:32
@odersky odersky reopened this Dec 7, 2016
@odersky odersky force-pushed the add-implicit-funtypes branch 2 times, most recently from af93547 to 3e82a23 Compare December 7, 2016 13:45
@notxcain
Copy link

notxcain commented Dec 7, 2016

Could you please provide with more examples where it is really useful (the one from article is very synthetic IMHO)? Not sure how it is different from using ReaderT for passing context. While this kind of implicits (contextual) make things harder to reason about.


/** An implicit function type */
class ImplicitFunction(args: List[Tree], body: Tree) extends Function(args, body) {
override def toString = s"ImplicitFunction($args, $body"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing end-paren?

@nafg
Copy link

nafg commented Dec 8, 2016

Thought while reading the blog post --- what if implicitly were redefined as def implicitly[A]: implicit A => A, then instead of def thisTransaction: Transactional[Transaction] = implicitly[Transaction] you could just use type inference with def thisTransaction = implicitly[Transaction]

(OT -- is there any chance of getting polymorphic function values one day?)

@acjay
Copy link

acjay commented Dec 8, 2016

This seems potentially really handy, but the one thing that's bugging me a bit is that if this is the most common application, it seems like it requires some nontrivial setup. First of all, thisTransaction (and its analogs for other contexts) would have to be imported all over the place, to take advantage of that syntax. Secondly, every apparent method output type has got to be decorated with the context the method requires for input. I can see this being really confusing, especially for output types that might already involve parameterized types. It seems like it could be better to annotate the input instead, somehow. Also, instead of having to use implicitly, directly or indirectly, a syntax that has that effect might help.

Suppose I could annotate an implicit function in the type parameter list, and then access the context directly:

  def f1[*Transaction](x: Int): Int = {
    [Transaction].println(s"first step: $x")
    f2(x + 1)
  }

I'm sure this syntax that drops implicitly probably conflicts with some existing Scala syntax, but it's not hitting me right away. It would be a bold move, but on the other hand, if implicit functions are a winning play for passing context, they could be ubiquitous in real Dotty code.

@liufengyun's dynamic parameters also seem to address my concerns, when it comes to the application of implicit functions for context-passing But do we know of any other killer applications of this feature? Shooting in the dark, but perhaps the ability to eta-convert methods with implicit parameter lists, preserving the implicitness?

If I understand @nafg's request for polymorphic function values, combined with implicit function types, functions and methods would almost be fully interchangeable. Default parameters and parameter names are the only things that come to mind as being left out. But now I think I've gone fully off on a tangent.

P.S. @nafg Heh, I had a very similar thought, I think: https://twitter.com/AlanJay1/status/806685555538980864, although I didn't make the connection between it and simplifying thisTransaction.

@nafg
Copy link

nafg commented Dec 8, 2016 via email

@liufengyun
Copy link
Contributor

@nafg To summarize my view: I think dynamic parameters can do better than implicit function types.

Dynamic parameters is based on the following paper (renamed to avoid confusion with Scala implicits):

Implicit parameters: dynamic scoping with static types, Jeffrey R. Lewis, POPL '00

The detailed argument is in the Gist.

@nafg
Copy link

nafg commented Dec 9, 2016

Here's another possible use case.
Some libraries, like Slick and Shapeless, have a lot of machinery happening based on implicits. The problem is that if I'm writing some polymorphic code, the errors about missing implicits are often very cryptic and it's hard to know what implicit parameters my method needs, and besides it's very tedious to add things like slick.lifted.Shape[Level <: ShapeLevel, -Mixed_, Unpacked_, Packed_], or all the operations you may need on your HList. If instead they would return implicit functions, and I would wrap them via function composition, perhaps the required typeclasses could just be inferred.

@odersky
Copy link
Contributor Author

odersky commented Dec 12, 2016

For discussions about the concept of implicit parameters and the blog post, let's go to:

https://contributors.scala-lang.org

Let's narrow this discussion here on the specifics of the implementation.

@odersky odersky force-pushed the add-implicit-funtypes branch from bee8e37 to 9d57d81 Compare December 13, 2016 14:33
@odersky
Copy link
Contributor Author

odersky commented Dec 13, 2016

Rebased to master

@odersky
Copy link
Contributor Author

odersky commented Dec 13, 2016

The last commits represent the second step to contectual abstraction in Scala. They make
implicit function types a close to zero-cost abstraction, by means of a phase that optimizes them
to plain methods.

* is expanded to two methods:
*
* def m(xs: Ts): IF = implicit (ys: Us) => m$direct(xs)(ys)
* def m$direct(xs: Ts)(ys: Us): R = E
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why this optimization is restricted to implicit functions? Done this way, this seems rather ad-hoc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reason is that the rewrite is based on the type of the result. For implicit function types we have a perfect match. The typing rules guarantee that every function that has an implicit function result type must be implemented by a closure. For normal functions this is not guaranteed, so we do not know whether the optimization is globally effective or not. It could make things worse, actually.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Blaisorblade, additionally note that normal function types are non-final and in case m is overriden in as subclass by a non closure with custom apply logic, you could have hard time figuring out what should $direct method do.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The typing rules guarantee that every function that has an implicit function result type must be implemented by a closure.

So I can't implement an implicit function type by returning a closure? Are those non-first-class?

Quite a few things suggest the following: A => B is a value type, while implicit A => B is a non-value type (IMHO a method type). I see why you're doing that, but that doesn't sound very orthogonal to me—this is a small thing, but that's the sort of thing which gives Scala its reputation for complexity. And while this fits in the heads of compiler hackers, the heads of (advanced) users also matter.

On Discourse I started sketching, as an alternative, a (limited) form of abstraction on non-value types (in particular, method types). I expect there are questions there too, but right now it seems to me you are in fact implementing a special-case of that.
(I don't propose abstract non-value types—I'd expect them to be type aliases, according to the same rules you use here, whatever they are exactly).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@odersky odersky Dec 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's say you have

 val cl: implicit A => B
 def f: implicit A => B = cl

Then by the rules of implicit expansion this will gve you an eta-expansion on the rhs of f:

def f: implicit A => B = implicit $x: A => cl($x)

That's what guarantees that the right-hand side of every method with implicit function result type is an implicit function value. For normal functions that guarantee does not hold, so we might well be pessimizing code with a shortcutting optimization.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, so here we inline the eta-expansion and emit a call to cl, while with normal functions we only have a non-inlined call to CL (which will be inlined anyway by the JVM). Fair enough.
Maybe some info about this should be part of the code docs?

@odersky
Copy link
Contributor Author

odersky commented Dec 14, 2016

/rebuild

sym.copy(
name = sym.name.directName,
flags = sym.flags | Synthetic,
info = directInfo(sym.info))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sort of transform needs to take some care about what annotations end up on the old and new methods, for things like @synchronized and @strictfp semantics depend on it. For general annotations, having a copy on both methods might break assumptions of the consumer of the annotations.

Representative bug fix in Scala: scala/scala#5540

Copy link
Member

@retronym retronym Dec 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same applies to the flags. The original sym might have OVERRIDE, but that doesn't guarantee that the direct method will override.

trait T[A] { def foo: A = ??? }
class C extends T[Transactional[Int]] {
  override def foo: Transactional[Int] = ??? // direct method won't be an override
}

Copy link
Member

@retronym retronym Dec 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confused myself above, synchronized is a flag (on class methods after uncurry) not an annotation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still don't do a systematic job for annotations. We just copy them wholesale when copying symbols. This should be guided by some meta-annotation scheme but that remains to be done. As to flags, I agree it's not clean to always copy "Override". I don't think it matters because we don't test the flag after the transform, but we should avoid it anyway. I'll add a change.

case Block(stats, expr) => cpy.Block(tree)(stats, directQual(expr))
case tree: RefTree =>
cpy.Ref(tree)(tree.name.directName)
.withType(directMethod(tree.symbol).termRef)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing that this transform is (correctly) skipped for:

scala> def foo(a: Any): Transactional[Int] = 42
def foo(a: Any): Transactional[Int]
scala> (if ("".isEmpty) foo("") else foo("")).apply("")

Because tree.qualifier.symbol would be NoSymbol.

But just wanted to check that I'm reading this correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's correct.

flags = sym.flags | Synthetic,
info = directInfo(sym.info))
if (direct.allOverriddenSymbols.isEmpty) direct.resetFlag(Override)
direct
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this force the current info transform on the base types? If not, the result might be non-determistic. I found this a bit fiddly to get right in an example i once created for how to do this in a compiler plugin.

Copy link
Contributor

@DarkDimius DarkDimius Dec 16, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will invoke infoTransforms, but this phase doesn't register one(it actually does, but it's identity), it forcefully updates denotations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@retronym Any infoTransforms in phases up to this one would be forced on the basetypes, yes.

}

val (remappedCore, fwdClosure) = splitClosure(mdef.rhs)
val originalDef = cpy.DefDef(mdef)(rhs = fwdClosure)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to reset the ABSTRACT flag on the original method, now that it always contains a forwarder?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, a Deferred method would not get a forwarder.

Run a typical dotty compiler scenario with implicit
contexts.
1. I noted java_all was not running(it took 0.01s to complete); fixed by
   changing the test directory.

2. We suspected tasty_bootstrap was gettng the wrong classpath and
   had a lot of problems getting it to print the classpatg. Fixed
   by refactoring the options we pass to tasty_bootstrap (it has
   to be -verbose in addition to -classpath). For the moment,
   both a turned off but we have to just swap a false to a true
   to turn them on together.
"Wrong number of args" only works for type arguments but was called also for
term arguments. Ideally we should have a WrongNumberOfArgs message that works for
both, but this will take some refactoring.
The Ref copier copies Idents and Selects, changing the name
of either.
This avoids denotation transforms when called at a later
phase because it cuts out current. Not needed in final
version of ShortcutImplicits, but I thought it was
good to have.
Optimizes implicit closures by avoiding closure
creation where possible.
Benchmark code to compare compilation schemes in
different scenarios. See results.md for explanations.
Introduce an option to not specialize monomorphic
targets of callsites.
Also, integrate Jason's test case with the conditional.

/** Should `sym` get a ..$direct companion?
* This is the case if (1) `sym` is a method with an implicit function type as final result type.
* However if `specializeMonoTargets` is true, we exclude symbols that are known
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you mean "false", not "true" here.

@odersky
Copy link
Contributor Author

odersky commented Dec 17, 2016 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet