This is already implemented with manifold's unit expressions, but is made richer and more readable via concatenative features. For instance, '1.5 * USD' is not as readable as simply '1.5 USD'. However, unit expressions leverage operators for dimensional arithmetic:
Force f = 5 kg * 9.807 m/s/s; // result: 49.035 Newtons
Area space = (20ft + 2in) * (37ft + 7.5in); // result: 758 37/48 ft²
All unit expressions internally store amounts as SI units, which enables interunit expressions.
Length height = 6 ft + 4 cm; // mix SI and US units
out.println(height.to(ft)); // display any unit
You aren't limited to units. Generally, any concatenative sequence can be implemented. Like ranges:
IntegerRange range = 1 to 5;
Here the `to` identifier's type implements reactions to Number types to produce range types, which enables:
for (int i: 1 to 5) {
out.println(i);
}
See the manifold project's Unit and Science modules:
Ha! I implemented "concatenative" programming for Java as Binding [1] (or Unit) expressions, but had no idea. Seriously, never came across that term before this post.
The idea with binding expressions is the type of expression A and the type of expression B implement "reactions" with one another in order to form a binding expression when they are lexically adjacent.
65 mph
The type of `mph` defines a post reaction method with the type of `65` as an argument that results in type Rate. As I understand it this is concatenative, right?
Another example:
Money payment = 1.5M USD;
There are tons of these.
Concatenative programming in general feels like it should have a more prominent place in mainstream languages. Just my take.
California policy makers' (ardent environmentalists) "Low Carbon Fuel Standard" require 10% corn-derived ethanol in gasoline sold in the state. Are they not environmentalisting hard enough?
Yes, Manifold already supports Java 15 multi-line strings (aka text blocks) like this:
var myValue = """
[>.js<]
function foo() {
return "hi";
}
foo();
""";
You can embed a resource fragment as either a declaration or a value. You use a string to embed a value fragment as with the JS example above. Note the [>.js<] header indicates the resource type for the string, similar to your annotation. As you surmised, an expression such as a string literal cannot be annotated.
> but is there really a need to have this kind of language interoperability at compile time?
Well, if you want to leverage Java's static type system (and why not?), the answer is, yes. I imagine you'd want type and member references to the other language to resolve statically using the compiler, right? Similarly, why not have the same functionality in your IDE? Plus code completion, usage searching, refactoring?
Now, as I mentioned in an earlier comment, the embedding part of this addresses just a small segment of use-cases e.g., scoped query editing. The vast majority of other cases work directly against resource files, type-safely. Read more about that here:
> Good IDE lets you move between the resource file and the code with a single click.
Well, not exactly. Most IDEs do NOT support clicking through a generated method call to the corresponding element in a resource file. Manifold is all about that. See it in action: http://manifold.systems/images/graphql.mp4
The Manifold fragments discussed here address only a narrow band of use-cases where embedding the resource improves the dev experience, like with queries and such. It's not for everyone, though.
At compile-time manifold-js[1] parses JS and generates Java types (stubs), which forward execution to rhino at runtime. Basically, JS seamlessly mapped onto Java's type system.
Hi, you can blame me for this. It's a Java compiler plugin[1], which is similar to an annotation processor, but can hook into the compiler at a much earlier stage, which allows it to contribute to all phases of the compiler, including the Parser phase. The Manifold plugin takes full advantage of this, hence its ability to analyze comments, contribute to bytecode generation, etc.
In most cases, the devs that should be concerned about debugging generated code are the authors of the generator. If there are bugs in released code, consumers of the API should file against them. In any respect, as I mentioned in a previous comment when a resource is selected you can view its generated source via Edit | View Java Source.
You can see the generated code in the IDE via Edit | View Java Source when a resource is selected. And you can debug it as well. Sort of the best of both worlds. What makes Manifold most interesting, however, is the entire dev experience when used in the IDE. This short screencast demonstrates some of that: http://manifold.systems/images/graphql.mp4
But the main idea with this is to avoid the pitfalls of conventional code generation such as GQL Code Generator. See "The Big Picture"[1] in the core docs.
It's both. If the popularity of new languages is any measure, static languages have quietly won the static v. dynamic war over the last few years: TypeScript, Kotlin, Swift, etc. Also considering the onslaught of static type checkers and compilers for otherwise dynamic languages such as Ruby, Python, etc. One must conclude static type analysis is everything, or at least it appears to be. IDEs are for the most part measured by how well they incorporate static analysis with features such as deterministic code completion, navigation, usage searching, refactoring, and the like. New IDEs for old static languages are finally arriving such as CLion. Not that IDEs can't be built for dynamic languages, they can, but they tend to be less efficient and less capable because static type analysis is so much more difficult to achieve due to the inherent nondeterminism.
Force f = 5 kg * 9.807 m/s/s; // result: 49.035 Newtons
Area space = (20ft + 2in) * (37ft + 7.5in); // result: 758 37/48 ft²
All unit expressions internally store amounts as SI units, which enables interunit expressions.
Length height = 6 ft + 4 cm; // mix SI and US units
out.println(height.to(ft)); // display any unit
You aren't limited to units. Generally, any concatenative sequence can be implemented. Like ranges:
IntegerRange range = 1 to 5;
Here the `to` identifier's type implements reactions to Number types to produce range types, which enables:
for (int i: 1 to 5) { out.println(i); }
See the manifold project's Unit and Science modules:
Units: https://github.com/manifold-systems/manifold/tree/master/man...
Science: https://github.com/manifold-systems/manifold/tree/master/man...