Integrated Repl in Editor for Java
In the company where I’m currently working we use https://www.jetbrains.com/mps/, which is a tool to define small programming languages and get very easily a projectional editor and all sort of things, think of IntelliJ, but you can define your own language.
The tool itself contains a language called BaseLanguage, which is basically Java, but defined in the tool and thus extensible.
The tool has also already a Console which allows to run Java statements and expressions.
Inspired by Clojure I would like to integrate the repl in the editor and allow to run statements directly without copy pasting to the Console, I want also to add something similar to the (comment ...) block which is discarded at compilation.
One of the problems that I already see is that Java is very stateful and so it’s hard to evaluate statements in isolation, so the idea would be that you accumulate statements in a buffer and then evaluate them when you have all the needed context maybe.
Now the question is: how would you want such a repl for Java to work? What could be a useful feature in your opinion?
I have used MPS. It is one of the greatest tools I know. But I always felt that the project recompilation is a burden. It feels for me like an heritage from Java, that you have to recompile, and MPS has no good answer for this. MPS feels all so dynamic and flexible. But in reality it is not. When I learnt Lisp, I immediately realised, that the REPL way of working is the right way to do things, and I had the impression, that MPS will never achieve this. MPS was my motivation to learn Clojure, and not Common Lisp. But I stopped using MPS. I would be very happy if you find a way to marry MPS and Clojure. Just having a Java REPL would not make much sense to me. Nobody would agree that the MPS base language is a good way for data oriented code. But this is what all these different pieces in MPS are. Go here, extract this, compare it, list it add, remove, and so forth. People want Python, Js, Julia or Clojure for this. Java is just a bad compromise, because MPS is just an IntelliJ plugin, and everything there is Java. All this is just my opinion. Please don’t take it too serious. Maybe only as inspiration.
Also interesting: https://mps-support.jetbrains.com/hc/en-us/community/posts/205826639-Why-MPS-is-better-than-lisp
The Clojure REPL evaluates s-expressions in the application's JVM. From MPS' console page (https://www.jetbrains.com/help/mps/mps-console.html) it seems that MPS executes ...stuff... in the IDE's JVM, where it "can access and modify the program's AST, display project statistic, execute IDE actions, launch code generation..."
Yes, is that a problem? At the moment we spend a bunch of time copying and pasting code from a class to the Console to try stuff, so maybe something useful would be just a shortcut to send the selected code to the console, something like append to the console.
I recently attempted something to that effect w/ C# to try and bring some of the repl juice (and left it behind because that's what happens to side projects) The ultimate entry point to the evaluator basically was a custom syntax of:
using* namespace? classdef* statement*
with the statement component being evaluated in a throwaway function (with the consequence of return stopping evaluation), with the only transformation being attempting to wrap the last statement in return if it was an expression statement.
If the user wanted to evaluate part of a file, the relevant usings and namespace would be ripped out of the file and the evaluated bit would be appended after that and forwarded to the above mechanism.Ah wow 😮, I didn’t get that far with my thinking. Could you give me a concrete minimal example of how that would look like?
for instance (c# but trivially workable to Java)
using Foo.Bar;
namespace Baz {
namespace Quux {
[|var x = Foo.Frobulate("foo");
if(Foo.Baz) {
return 3;
}
await Quux.Lol();
4;]|
}
}
(with the [|...|] marking which part was eval'd by user)
would turn into
using Foo.Bar;
namespace Baz.Quux;
class G_69420
{
async Task Run() {
return new(async () => {
var x = Foo.Frobulate("foo");
if(Foo.Baz) {
return 3;
}
await Quux.Lol();
return 4;
});
}
}
then the file would be compiled and the function run.
I'm at my job so I had to make it on the fly but you should get the idea.