-
Notifications
You must be signed in to change notification settings - Fork 323
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
Automatically apply from
conversion when runtime argument check fails
#7009
Conversation
I am concerned that this might result in hard-to-find bugs. If you write a bad method call that should fail, and it succeeds because of a conversion you weren't aware about, you'll get unexpected behavior that will be tricky to find, because it is made possible by a call to On the other hand, I am not familiar with the |
@JaroslavTulach what is the status of this issue? |
It is a draft. |
Running benchmarks to get official results for the new ListBenchmarks. The results are as expected:
all relevant benchmarks need 140-150ms per operation, but |
The case with conversion shall now be faster since ec5a3b3 - running benchmarks to verify. <case>
<label>org.enso.interpreter.bench.benchmarks.semantic.ListBenchmarks.mapConvOverList</label>
<scores>
<score>144.07712597041146</score>
</scores>
</case> there seems to be a slowdown in plain <case>
<label>org.enso.interpreter.bench.benchmarks.semantic.ListBenchmarks.mapOverList</label>
<scores>
<score>150.43897446282224</score>
</scores>
</case> and it needs to be investigated. |
Benchmarking most recent changes at benchmark run.... existing benchmarks seem to be fine... |
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 is absolutely AMAZING! It also means that we are closer and closer to the .to
method, right? Also, would it be possible to notify GUI where such conversion was applied? We would then display that using a special connection style (e.g. dashed one).
@GregoryTravis your concerns are very valid. Every implicit thing has the downside of code being more complex to trace. That's why it's important to visually indicate all such places - both on the graph and in the text editor. Anyway, such auto-conversions will help make the graph clean and fast to work with and I believe they can become one of the most powerful features of Enso.
+1
Yes, we are able to trace when a conversion happens using GraalVM Insight (a toy I love). Imagine following from Standard.Base import all
meaning (v : Text) = "Meaning of world " + v
main = meaning 42
Text.from (that : Number) = that.to_text which will do the automatic conversion when passing number
Let's now use the GraalVM's Insight capabilities by creating a tracing script to print out a message whenever a insight.on('return', (ctx, frame) => {
let that = frame.that;
let to = ctx.returnValue(frame);
print(`Converting via ${ctx.name} at ${ctx.source.name}:${ctx.line}`
+ ` from ${that}:${typeof that} to ${to}:${typeof to}`);
}, {
rootNameFilter : '.*from',
roots : true
}); and then execute with additional enso$ JAVA_OPTS=-Dpolyglot.insight=trace.js ./built-distribution/enso-engine-0.0.0-dev-linux-amd64/enso-0.0.0-dev/bin/enso --run convert.enso
Converting via convert::Standard.Base.Data.Text.Text::from at convert.enso:7 from 42:number to 42:string
'Meaning of world 42' and we can see that the
...we just need to design a way to provide such information to the IDE and then display it. That will need some future work, CCing @4e6.
I am sure we can do it in the graph - e.g. when the program is running - easily. Alas, I don't know how to do the same in the text editor - e.g. just by static analysis. |
This PR proves there is no overhead when applying the conversions - e.g. that the meaning (42.to Text) one can simply call |
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.
nits
} | ||
|
||
static ReadArgumentCheckNode build(String argumentName, Type[] expectedTypes) { | ||
if (expectedTypes == null || expectedTypes.length == 0) { |
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.
That's a bit unusual. IIRC for all nodes we assume that build
will return the node, no exceptions?
} | ||
|
||
private static boolean isAllFitValue(Object v) { | ||
return v instanceof DataflowError |
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.
speaking of DataflowError
logic being distributed ;)
} | ||
|
||
ApplicationNode findConversionNode(Type from) { | ||
var convAndType = findConversion(from); |
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.
from
can be null
I think, but I don't think we handle that nicely in findConversion
, do we?
Just please note that in many cases we put parameters directly on a node, and this conversion may also happen directly there and there will be no connection to stylize. i.e. if we have
instead of
there is no connection between Maybe we should consider to then add some indication for these cases as well? Possibly some dashed frame over the parameter, which when hovered says which conversion has been applied? |
) Engine benchmark downloader tool [bench_download.py](https://github.com/enso-org/enso/blob/develop/tools/performance/engine-benchmarks/bench_download.py) can now plot multiple branches in the same charts. The tooltips on the charts, displayed when you hover over some data point, are now broken for some branches. I have no idea why, it might be a technical limitation of the Google charts library. Nevertheless, I have also extended the *selection info* section, that is displayed under every chart, where one can see all the important information, once you click on some data point. Options added: - `--branches` specifies list of branches for which all the benchmark data points will be in the plots. The default is `develop` only. - `--labels` that can limit the number of generated charts. This PR also **deprecates** the `--compare` option. There is no reason to keep that option around since we can now plot all the branches in the same charts. An example for plotting benchmarks for PR #7009 with ``` python bench_download.py -v --since 2023-07-01 --until 2023-07-11 --branches develop wip/jtulach/ArgumentConversion ``` is: ![image](https://github.com/enso-org/enso/assets/14013887/62010850-79d2-4c6c-92bc-9627bb4c6a0b) # Important Notes - Deprecate `--compare` option - Add `--labels` option - Add `--branches` option
Pull Request Description
Enhancing
ReadArgumentNode
to applyfrom
conversions when the argument type check fails. For example there is no need to type42.to_text
when requiring aText
argument. The conversion will happen automatically.Details
#6790 has introduced ability to opt-in and check type of arguments where needed (from a standard library perspective). These checks are slowly appearing in the Enso code base. They help us catch typing errors as soon as possible and report errors to users in a meaningful way. User can then change their code and apply necessary conversions themselves. However there is no need for such manual conversions!
We can automatically apply
from
conversion when the argument type doesn't fit the ascribed type and such a conversion is available in the module scope. We can do such conversions effectively - almost at no performance cost as benchmarks added by this PR demonstrate.Non-goal
Changing the way conversions are imported into scope of a module isn't a goal for this PR. The way conversions are found remains unchanged by this PR. If a
from
conversion was found "manually" before, it is going to be found "automatically" now. Designing new ways for conversions to be located is out of scope of this PR. However as #7271 demonstrates, the current behavior may actually be good enough.Future Work
These automatic conversions work great with runtime type classes. Implementing such runtime type classes with automatic conversions from any value is going to be possible when this PR is integrated.
It is (almost certainly) possible to add support for
&
operator without any performance penalty - e.g. one could write(a: Ord & Eq)
and the type system would verify that the argumenta
supports all the required conversions - toOrd
as well as toEq
. Implementation of this is out of scope of this PR.Checklist
Please ensure that the following checklist has been satisfied before submitting the PR:
Scala,
Java,
ListBenchmarks.mapOverList
benchmark