-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Conversation
@@ -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 (...)") |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
There was a problem hiding this 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:
- How does this interact with specialization of the FunctionN types for lower N?
- 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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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:
- I wrote an Interactive, reader-friendly online essay about coeffects
- All the glorious details are in my PhD thesis - relevant sections for the discussion above are 2.2, 3.2.1 and 5.6.
- Someone already mentioned our ICFP paper, but the older ICALP paper might be easier to read if you care only about implicit parameters.
- 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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/peaple/people
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 (?)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)
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. |
I drafted the immature idea of dynamic parameters here, which is marginally related to this discussion. |
af93547
to
3e82a23
Compare
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 |
|
||
/** An implicit function type */ | ||
class ImplicitFunction(args: List[Tree], body: Tree) extends Function(args, body) { | ||
override def toString = s"ImplicitFunction($args, $body" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing end-paren?
Thought while reading the blog post --- what if (OT -- is there any chance of getting polymorphic function values one day?) |
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, Suppose I could annotate an implicit function in the type parameter list, and then access the context directly:
I'm sure this syntax that drops @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 |
On Thu, Dec 8, 2016 at 1:48 AM Alan Johnson ***@***.***> wrote:
@nafg <https://github.com/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.
This seems potentially really handy, but the one thing that's bugging me a
bit is if this is the most common application, it seems like it requires
some nontrivial setup. First of all, thisTransaction and friends would
have to be imported all over the place. 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.
@liufengyun <https://github.com/liufengyun>'s dynamic parameters seem to
capture this concern, when it comes to the context application of implicit
functions, anyway. 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 <https://github.com/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.
Hmm those would be nice too. :) But how would you encode them?
… —
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1775 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAGAUBNgax2AL9XZmiAtFfrqejY7xETWks5rF6gwgaJpZM4LED-h>
.
|
@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. |
Here's another possible use case. |
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. |
bee8e37
to
9d57d81
Compare
Rebased to master |
The last commits represent the second step to contectual abstraction in Scala. They make |
* 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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
/rebuild |
sym.copy( | ||
name = sym.name.directName, | ||
flags = sym.flags | Synthetic, | ||
info = directInfo(sym.info)) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
}
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
Good catch.
…On Sat, Dec 17, 2016 at 7:00 PM, Guillaume Martres ***@***.*** > wrote:
***@***.**** commented on this pull request.
------------------------------
In compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala
<#1775 (review)>:
> + */
+ final val specializeMonoTargets = true
+
+ class Transform extends TreeTransform {
+ def phase = thisTransform
+
+ override def prepareForUnit(tree: Tree)(implicit ctx: Context) = new Transform
+
+ /** A map to cache mapping local methods to their direct counterparts.
+ * A fresh map is created for each unit.
+ */
+ private val directMeth = new mutable.HashMap[Symbol, Symbol]
+
+ /** 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
I think you mean "false", not "true" here.
—
You are receiving this because you modified the open/close state.
Reply to this email directly, view it on GitHub
<#1775 (review)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAwlVvYb8m6V0oScR_t2N8dYqICTy0dOks5rJCM-gaJpZM4LED-h>
.
{"api_version":"1.0","publisher":{"api_key":"
05dde50f1d1a384dd78767c55493e4bb","name":"GitHub"},"entity":
{"external_key":"github/lampepfl/dotty","title":"
lampepfl/dotty","subtitle":"GitHub repository","main_image_url":"
https://cloud.githubusercontent.com/assets/143418/17495839/a5054eac-5d88-
11e6-95fc-7290892c7bb5.png","avatar_image_url":"https://
cloud.githubusercontent.com/assets/143418/15842166/
7c72db34-2c0b-11e6-9aed-b52498112777.png","action":{"name":"Open in
GitHub","url":"https://github.com/lampepfl/dotty"}},"
***@***.*** commented on
#1775"}],"action":{"name":"View Pull Request","url":"https://
github.com/lampepfl/dotty/pull/1775#pullrequestreview-13450284"}}}
--
Prof. Martin Odersky
LAMP/IC, EPFL
|
This is the first step to bring contextual abstraction to Scala.