343 karma · joined August 12, 2023
For a more pragmatic take on static metaprogramming, the manifold project[1] for Java is worth a look. Unlike F#, which leans toward expansive schemas, Manifold focuses on contained, compile-time integrations—handling JSON, XML, SQL, GraphQL, and even other languages in a seamless, type-safe way.
As for type providers in general, I don't think databases are the best example of their typical use case. Most type providers don’t interact with external systems; they usually parse schemas, configuration files, or other structured data to generate strongly typed representations. The database-backed approach is just one variant, not the norm.
The problem with these various "integrity by default" options is that, in most cases, granting access to one effectively grants access to all. For instance, JNI, agent libraries, and JPMS options can each be used to bypass restrictions, making the separation between them largely illusory. Integrity, as framed here, is ultimately binary.
The unfortunate reality of the "integrity by default" crusade is that applications relying on libraries and tools that modify internals will continue to do so. The JDK hasn’t filled any gaps—it has only made an already delicate situation worse.
1. https://github.com/manifold-systems/manifold/tree/master/man...
>
// commonly used unit abbreviations e.g., m, ft, hr, mph, etc.
import static manifold.science.util.UnitConstants.*;
...
Length l = 5m; // 5 meters
Length height = 5 ft + 9.5 in;
Length distance = 80 mph * 2.3 hr;
Force force = 5kg * 9.807 m/s/s; // 49.035 Newtons
Force f = 49.035 kg m/s/s;
force == f // true
force == 49.035 N // true
1. https://github.com/manifold-systems/manifold/tree/master/man...*
1. https://github.com/manifold-systems/manifold/blob/master/doc...
Go can't declare adherence up front, and in my view that’s a problem. Most of the time, explicitly stating your intent is best, for both humans reading the code and tools analyzing it. That said, structural typing has its moments, like when you need type-safe bridging without extra boilerplate.
The Cavendish—the dominant grocery store banana—is one of the few that turns mushy and unpleasant when it does.
Huge drawback? Quite the opposite. Unintentional implementation is a terrible trade for readability and type-safety, hence the trick the prior commenter mentioned.
In fact, structural typing is best when limited to the far less used case where an interface is designed to reflect functions from existing types. It's useful here, otherwise it's a hinderance for the primary case where a type _intends_ to implement an interface, which is what nominal typing is for.
Go was designed as a systems language, and the choice for structural typing served that goal in terms of performance enhancements. But used as a general purpose language, as the trick above (and others) demonstrate, Go's structural typing is less suitable compared with nominal typing.
I haven't been keeping score, but I've read other articles like this one claiming Go as an enterprise language alternative to Java. Not much concerning Java as a systems language, but that makes good sense. I see Go v. C/Rust, not Go v. Java/Kotlin. Just my take.
Yes, and while it uses the compiler to ensure a type implements an interface, it's still a trick that exposes a large hole in the language... and begs for it to be filled. Most importantly, it still doesn't make the code much easier to read and understand.
>At least IntelliJ IDEs will always make it clear (or less foggy)
Indeed. Go benefits hugely from IntelliJ, or to be specific the IJ plugin API.
I suppose exclusively structural typing would be more acceptable if Go supported _real_ interface composition, like Scala with traits or true delegation via the manifold project[1] for Java. But that's missing as well e.g., does not inherently fix the Self problem, etc.
Considering Go's initial goal, which was IIRC a better systems language, then yeah, sure it's an improved C. But now that Go is routinely compared with Java/Kotlin and friends, I personally don't see it, particularly wrt the type system, to be taken seriously as a Java contender. Shrug.
1. https://github.com/manifold-systems/manifold/blob/master/man...
Yep. I'm not a particularly big fan of Google languages, but focusing on bridging legacy code is brilliant.
Would have been interesting if Oracle had done the same with Java shortly after acquiring it. Java is so hemmed in by legacy compatibility it basically can't make any significant language or VM changes without making harsh compromises. Of course, Oracle would likely have supported legacy Java indefinitely, and made a fortune supporting/licensing it.
Yeah, being smart definitely helps with making money, but honestly, that bar’s not as high as people think. There’s a bunch of other stuff that matters too, like being likable and humble. But if we're being real, you’ll probably get richer with cunning and greed. And history is pretty clear, the more opportunistic ones tend to stack higher piles. Money is cruel, man.
Who can argue with the massive reduction in complexity and finally ditching javascript?
1. https://github.com/manifold-systems/manifold/blob/master/man...
1. https://github.com/manifold-systems/manifold/blob/master/man...
In my view, mainstream languages like Java should offer static metaprogramming for library authors to make tooling like Rails possible and more approachable in IDEs.
The manifold project[1] has something like this for SQL, but it’s obviously providing its own static metaprogramming layer over Java. Shrug.
1. https://github.com/manifold-systems/manifold/blob/master/man...
Agreed.
> 3) The biggest challenge about data models is not auto-generated code
I would say auto-generated code is most definitely the harder problem to solve, and I’d also go out on a limb and say it is THE problem to solve.
Whether it’s JSON, XML, JavaScript, SQL, or what have you, integrating both data and behavior between languages is paramount. But nothing has changed in the last 40+ years solving this problem, we still generate code the same clumsy way… Chinese wall between systems, separate build steps, and all the problems that go with it.
Something like project manifold[1] for the jvm world is in my view the way forward. Shrug.
Aren’t mods more or less the primary justification for building a server like this?
Although Rust may in some ways improve performance, is that a better trade for the huge reduction in potential mods?
For instance, physical dimensions store values as SI units, regardless of supplied unit, but retain the supplied unit type as a display hint.
Here, the Length dimension illustrates working with amounts of any unit.
Length orbit = 250 mi;
Length moon = 238000 km;
Length landing = moon + orbit;
display(landing); // prints 148,136.344 mi
display(landing.to(km)); // prints 238,402.336 km
// where
display(landing.to(mi) == landing.to(km)); // prints true
// perhaps a better example
Rate rate = 1.41 km/s;
Time duration = 76 hours;
Length distance = rate * duration;
// make your own unit constants
RateUnit kps = km/s;
rate = 1.41 kps;
You can play with the manifold-science[1] project, it implements the complete SI physical dimensions and many derived dimensions along with comprehensive operator overloading[2] support etc. to make working with and reading code using quantities more productive in Java.
1. https://github.com/manifold-systems/manifold/tree/master/man...
2. https://github.com/manifold-systems/manifold/tree/master/man...
Right. As I mentioned earlier exchange rate services would need to be integrated into the API.
Also, exchange rate services would not be needed until a conversion is necessary. For instance, when adding money of different currencies internally the Money type maintains currencies separately, subtotals for USD, EUR, etc. are managed. If at a later time the sum of Money should be displayed, deposited, etc. as a single currency, only then is an exchange service involved.
Money cost = 25M USD;
cost += 12M EUR;
display(cost.to(EUR));