Skip to content

Comments

Enso supports evaluation with arguments#14739

Open
JaroslavTulach wants to merge 18 commits intodevelopfrom
wip/jtulach/EvalWithArguments
Open

Enso supports evaluation with arguments#14739
JaroslavTulach wants to merge 18 commits intodevelopfrom
wip/jtulach/EvalWithArguments

Conversation

@JaroslavTulach
Copy link
Member

@JaroslavTulach JaroslavTulach commented Feb 9, 2026

Pull Request Description

Important Notes

Checklist

Please ensure that the following checklist has been satisfied before submitting the PR:

  • All code follows the
    Scala,
    Java,
  • Unit tests have been written where possible.

@JaroslavTulach JaroslavTulach marked this pull request as draft February 9, 2026 11:50
@JaroslavTulach JaroslavTulach requested a review from kazcw February 18, 2026 06:14

var imports =
originalSrc.lines().takeWhile(isImportStatement).collect(Collectors.joining("\n"));

Copy link
Member Author

@JaroslavTulach JaroslavTulach Feb 18, 2026

Choose a reason for hiding this comment

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

  • This kind of string splitting and concatenation is far from perfect
    • from a parser perspective this is a ridiculous attempt
    • but it gets the job done for now
  • What's needed?
    • Enso code snippets without imports are not very useful except REPL
      • hence we need to be able to import ... and from ... import ...
    • Coding without data structures is hard as well
      • hence we may need an ability to specify new type in the snippet
  • It can all be done with intrinsic string manipulation ...
  • but maybe there is a way to do this in TreeToIr, what do you think @kazcw?

Copy link
Contributor

@kazcw kazcw Feb 18, 2026

Choose a reason for hiding this comment

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

A question first: What do the source locations look like?

  • Foreign-function based: Source locations would be within the "file" constructed from the foreign function's string (I think this is what the current implementation does)
  • Host-module based: Source locations reflected the source code in the module. This might be more helpful for debugging. It also makes the IR more "compatible" for reuse, see below.

So, there are different ways to approach making things from the "host" module visible in the "foreign" module:

  • string operations before parsing the foreign module (as in this current implementation)
  • IR operations after parsing the foreign module. This could be very simple and efficient. The main issue is source references. The IR we would want to copy has host-module based locations; if the new source has foreign-function based locations, we can't just mix them (Unless IR knows its origin file somehow? But source references are just line:column). If we assign host-module based locations to everything, then combining the newly parsed "module" with parts of the host module is straightforward.
  • semantic operations: I could imagine the imports and type definitions being "executed" just once, to populate some sort of environment, within which functions from the host module, including foreign functions, could be run. But I don't know what would be involved in doing it that way, this is in later compiler/runtime stages than I am familiar with at all.

Copy link
Member Author

@JaroslavTulach JaroslavTulach Feb 19, 2026

Choose a reason for hiding this comment

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

What do the source locations look like?

  • while working on this PR I discovered some problems with source locations
  • evaluation of Debug.eval was using incorrect source file
    • it used the source that contained Debug.eval
    • rather the source of the code snippet
    • sometimes (when the snippet was longer than original file) that might even lead to exceptions
  • I improved the situation in c43d4e7
  • I should probably separate that to its own PR with a test...
  • done in Avoid invalid source section bounds error in Debug.eval #14784

Foreign-function based: Source locations would be within the "file" constructed from the foreign

Copy link
Member Author

@JaroslavTulach JaroslavTulach Feb 19, 2026

Choose a reason for hiding this comment

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

  • IR operations after parsing the foreign module. This could be very simple and efficient. The main issue is source references. .... If we assign host-module based locations to everything, then combining the newly parsed "module" with parts of the host module is straightforward.
  • I think the biggest problem of this PR is that there are "two sources"
  • one defining lambda which does imports and then invokes Debug.eval
  • the other one is expression body send as an argument to Debug.eval

Can we merge the two sources together into one? Isn't the expression body parsed in some special way (expression only)? I think it is:

Copy link
Member Author

@JaroslavTulach JaroslavTulach Feb 19, 2026

Choose a reason for hiding this comment

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

  • another thought: Do we want to change the syntax to allow this kind of expression body in .enso file?
  • because if one follows the Insight in Enso tutorial one gets a file like this:
obrazek
  • our existing VSCode/NetBeans support marks that file as syntactically incorrect!

@JaroslavTulach JaroslavTulach added -compiler CI: No changelog needed Do not require a changelog entry for this PR. labels Feb 18, 2026
@JaroslavTulach JaroslavTulach marked this pull request as ready for review February 18, 2026 16:42
@JaroslavTulach JaroslavTulach added the CI: Clean build required CI runners will be cleaned before and after this PR is built. label Feb 19, 2026
@JaroslavTulach
Copy link
Member Author

JaroslavTulach commented Feb 19, 2026

Insight in Enso Manual

Builds on top of Insight & Enso Manual that required use of JavaScript. This manual is fully relying on Enso language.

Create Insight Script

Simple script to start with is for example methods.js:

from Standard.Base import all

IO.println "Initializing Insight: "+(Meta.type_of insight).to_text

calling ctx frame =
    IO.println "Calling "+ctx.name

when = Dictionary.empty
    . insert "roots" True
    . insert "rootNameFilter" ".*main.*"

insight.on "enter" calling when

The insight variable is automatically provided. Learn more about writing Insight Scripts:

Usage in CLI

Execute either a script:

sbt:enso> runEngineDistribution --vm.D=enso.dev.insight=methods.enso --run test/Base_Tests Nic
Initializing Insight: (Error: (Compile_Error.Error 'unknown type_of for com.oracle.truffle.tools.agentscript.impl.AgentObject@710afd47'))
Calling enso_dev.Base_Tests.Main::enso_dev.Base_Tests.Main::main
Calling Main.main.suite
Calling Suite_Config.find_caller_script.find_main
Calling Suite_Config.find_caller_script.find_main
Calling Suite_Config.find_caller_script.find_main
Calling Suite_Config.find_caller_script.find_main


0 tests succeeded.
0 tests failed.
0 tests skipped.
0 groups skipped.

var originalSrc = src.getCharacters().toString();

Predicate<String> isImportStatement =
(line) -> line.startsWith("import ") || line.startsWith("from ") || line.trim().isEmpty();
Copy link
Contributor

Choose a reason for hiding this comment

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

Why an empty line is considered an import statement deserves a comment

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, probably a good idea to always do line.trim() in case of misaligned/malformed code

Copy link
Member Author

Choose a reason for hiding this comment

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

  • this kind of "source manipulation" is the most fragile part of this PR
  • I put the check for empty line there because:
    • it is common to separate imports into group in our code, for example:
    • from Standard.Base import all
      import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
      import Standard.Base.Runtime.Ref.Ref
      from Standard.Test import all

always do line.trim()

  • space before from Standard.Base import all is a syntax error
  • this all shows that doing IR manipulations (as discussed with Keziah) would be the preferred option

var lambdaArgs = argNames.stream().collect(Collectors.joining("-> "));
var lambdaCode =
"""
import Standard.Base
Copy link
Contributor

Choose a reason for hiding this comment

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

Assumption that Standard.Base is always available deserves a comment. Can we make eval a builtin?

Copy link
Member Author

Choose a reason for hiding this comment

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

  • Debug.eval is a builtin - but it still needs to be imported
  • as written here Enso supports evaluation with arguments #14739 (comment) this approach with "two sources" may be the root of the problem
  • however it is the only one that is known to solve the issue of "evaluation with arguments" right now

mergify bot pushed a commit that referenced this pull request Feb 20, 2026
- extracted from #14739
- source locations for reports of errors in `Debug.eval` scripts were wrong
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

-compiler CI: Clean build required CI runners will be cleaned before and after this PR is built. CI: No changelog needed Do not require a changelog entry for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants