The Forum for Discussion about The Third Manifesto and Related Matters

Please or Register to create posts and topics.

Life after D with Safe Java

PreviousPage 8 of 11Next
Quote from tobega on April 23, 2021, 7:28 am
Quote from dandl on April 22, 2021, 7:53 am
Quote from tobega on April 21, 2021, 3:27 pm
Quote from dandl on April 21, 2021, 10:56 am
Quote from Dave Voorhis on April 21, 2021, 8:22 am
Quote from dandl on April 21, 2021, 12:44 am

It wasn't meant to be an analysis. Just pointing out that your bullet points are vague enough to mean anything from cutting-edge future advancements to crude legacy languages. E.g., "getting rid of ... type declarations" could mean type inference per modern languages, or literally no type declarations and "type inference" of a handful of primitive types per vintage BASIC.

I think you're trying to read them backwards, as a list of inclusions rather than a list of exclusions. Assume you start with the latest Java/C#/etc: this is what we try to take out. There is another list of things we might want to add, including getting things back from C++ (meta, unions, type aliases, value types) and elsewhere.

I just mean they're vague, and subject to very broad interpretation.

Please, please, please do not bring back C++-style "meta", unions (if you mean what I think you mean), and type aliases. Java not having these is one of its strengths.

C++lets you do things (unsafely) that you can't do in Java. I want to be able to do them, but safely.

The meta I want is to solve this problem. I have just spent a couple of hours writing code like this for a large number of named variables:

Console.Writeln($"version="{a.Version}");
b.version = a.Version
Console.Writeln($"version="{b.version}");

This is debugging code, not production, and I don't care if the macro that does this for me offends your sensibilities. Simple text substitution is enough to save a mountain of typing, and we now have the tech that will allow us to do this safely. I want it.

C unions did two things: structural overlays (which is actually undefined behaviour) and simple algebraic types (along with struct). I want the algebraic types: don't  you?

C typedefs allow you to type check usage of primitive types. We used them extensively in Powerflex to manage the various compiler differences (such as signed/unsigned, short/long) but they're also really useful to distinguish (say) between an integer used as a count, one used as an index and one used as a length. There is no downside.

But I mention that not to get into a debate about what should/shouldn't be included, but to point out that it's almost inevitable that everyone you ask will have a different vision of what should/shouldn't be included in some C++/C#/Java successor. What I think should/shouldn't be there will differ from what you think should/shouldn't be there, and likewise for Ant, Erwin, Tobega, etc., etc.

I think the best you can do here is write something that scratches your own personal itches, and hope that enough other developers share the same itches to appreciate your, er, itch-scratcher.

I have very few specific itches: whatever gives me safer-shorter-higher, meta and fits in will be just fine. But it's totally wrong to think about what to add until you know for sure what you're prepared to give up to get it.

I keep waiting for the revolutionary vision of how we should program in a safer-shorter-higher way, but we're still at a laundry list of incremental changes which we can't all agree are improvements.

I'm still working on the laundry list. See post.

My presumption is that you need to write less code to leave room for getting more done. But I have a couple of targets:

  • a data structure that embodies structure at a higher level than the types we know and love. I have a data model of 20 tables, with various key and other constraints, and I would like to check at compile time that the operations I code for it will not violate any constraints.
  • Ditto for a graph structure, etc.
  • A template that generates an HTML page from an SQL query (the query, not the result set).
  • Templates to transform SQL <=> Json <=> XML <=> etc.

So far, based on your requirements, I think I would recommend you to take a look at Ada. I think a lot would be won if we all did, but it's not exactly a new revolutionary thing.

I don't think so.

Just saying, Ada has your complete list of C features in a safe way.

It's notable how often -- at least subjectively -- Ada shows up on lists of languages with notionally "cutting edge" features, like dependent typing. See https://en.wikipedia.org/wiki/Dependent_type and its "Comparison of languages with dependent types."

I'm the forum administrator and lead developer of Rel. Email me at dave@armchair.mb.ca with the Subject 'TTM Forum'. Download Rel from https://reldb.org
Quote from Dave Voorhis on April 23, 2021, 10:53 am
Quote from tobega on April 23, 2021, 7:28 am
Quote from dandl on April 22, 2021, 7:53 am
Quote from tobega on April 21, 2021, 3:27 pm
Quote from dandl on April 21, 2021, 10:56 am
Quote from Dave Voorhis on April 21, 2021, 8:22 am
Quote from dandl on April 21, 2021, 12:44 am

It wasn't meant to be an analysis. Just pointing out that your bullet points are vague enough to mean anything from cutting-edge future advancements to crude legacy languages. E.g., "getting rid of ... type declarations" could mean type inference per modern languages, or literally no type declarations and "type inference" of a handful of primitive types per vintage BASIC.

I think you're trying to read them backwards, as a list of inclusions rather than a list of exclusions. Assume you start with the latest Java/C#/etc: this is what we try to take out. There is another list of things we might want to add, including getting things back from C++ (meta, unions, type aliases, value types) and elsewhere.

I just mean they're vague, and subject to very broad interpretation.

Please, please, please do not bring back C++-style "meta", unions (if you mean what I think you mean), and type aliases. Java not having these is one of its strengths.

C++lets you do things (unsafely) that you can't do in Java. I want to be able to do them, but safely.

The meta I want is to solve this problem. I have just spent a couple of hours writing code like this for a large number of named variables:

Console.Writeln($"version="{a.Version}");
b.version = a.Version
Console.Writeln($"version="{b.version}");

This is debugging code, not production, and I don't care if the macro that does this for me offends your sensibilities. Simple text substitution is enough to save a mountain of typing, and we now have the tech that will allow us to do this safely. I want it.

C unions did two things: structural overlays (which is actually undefined behaviour) and simple algebraic types (along with struct). I want the algebraic types: don't  you?

C typedefs allow you to type check usage of primitive types. We used them extensively in Powerflex to manage the various compiler differences (such as signed/unsigned, short/long) but they're also really useful to distinguish (say) between an integer used as a count, one used as an index and one used as a length. There is no downside.

But I mention that not to get into a debate about what should/shouldn't be included, but to point out that it's almost inevitable that everyone you ask will have a different vision of what should/shouldn't be included in some C++/C#/Java successor. What I think should/shouldn't be there will differ from what you think should/shouldn't be there, and likewise for Ant, Erwin, Tobega, etc., etc.

I think the best you can do here is write something that scratches your own personal itches, and hope that enough other developers share the same itches to appreciate your, er, itch-scratcher.

I have very few specific itches: whatever gives me safer-shorter-higher, meta and fits in will be just fine. But it's totally wrong to think about what to add until you know for sure what you're prepared to give up to get it.

I keep waiting for the revolutionary vision of how we should program in a safer-shorter-higher way, but we're still at a laundry list of incremental changes which we can't all agree are improvements.

I'm still working on the laundry list. See post.

My presumption is that you need to write less code to leave room for getting more done. But I have a couple of targets:

  • a data structure that embodies structure at a higher level than the types we know and love. I have a data model of 20 tables, with various key and other constraints, and I would like to check at compile time that the operations I code for it will not violate any constraints.
  • Ditto for a graph structure, etc.
  • A template that generates an HTML page from an SQL query (the query, not the result set).
  • Templates to transform SQL <=> Json <=> XML <=> etc.

So far, based on your requirements, I think I would recommend you to take a look at Ada. I think a lot would be won if we all did, but it's not exactly a new revolutionary thing.

I don't think so.

Just saying, Ada has your complete list of C features in a safe way.

It's notable how often -- at least subjectively -- Ada shows up on lists of languages with notionally "cutting edge" features, like dependent typing. See https://en.wikipedia.org/wiki/Dependent_type and its "Comparison of languages with dependent types."

Yes, I think it probably got discarded a little too quickly. Notably I think must be the language that had the most solid requirements gathering and had 16 design teams competing to meet them. The winning design was heavily criticized for its complexity and it was a beast to compile in 1985, but it would seem that the designer did a really good job. Despite the complexity, users say that they still are able to mostly understand features they haven't seen before. The documentation apparently has extensive motivations for the existence of each feature, which helps to internalize it. It just seems to have gotten better with the updates as well.

Quote from Dave Voorhis on April 23, 2021, 9:22 am

It isn't, and it's why APL was coined a "read-only language" back in the day. Shooting strictly for "shorter" makes code harder to read, and that harms productivity.

There's a balance to be struck between terseness, brevity, readability, and verbosity. Likewise for composability, expressivity, intuitiveness, ease of learning, or simply simplicity.

 

WRITE-only ???

Quote from Erwin on April 23, 2021, 3:47 pm
Quote from Dave Voorhis on April 23, 2021, 9:22 am

It isn't, and it's why APL was coined a "read-only language" back in the day. Shooting strictly for "shorter" makes code harder to read, and that harms productivity.

There's a balance to be struck between terseness, brevity, readability, and verbosity. Likewise for composability, expressivity, intuitiveness, ease of learning, or simply simplicity.

 

WRITE-only ???

Oops. Yes.

I go fix now.

I'm the forum administrator and lead developer of Rel. Email me at dave@armchair.mb.ca with the Subject 'TTM Forum'. Download Rel from https://reldb.org
Quote from Dave Voorhis on April 23, 2021, 10:48 am

A common myth is that writing clearly gets in the way of writing productively.

It doesn't. Writing clear code improves productivity, because the first reader is the author.

The "simplest thing that can possibly work" refers to algorithmic and structural simplicity. It doesn't mean the fewest keystrokes, single-letter variable names, monolithic functions, and unreadably-chained eye-watering expressions.

I beg to annotate.

"Writing productively" clearly refers ***exclusively*** to the process of writing, i.e. the process of creating new code.  And yes, in that particular narrow view of "writing", "writing clearly" absolutely and undoubtedly does get in the way of "productivity".  The loss of productivity that comes along with "not writing clearly" is incurred only later.

And so yes, "writing clear code" does indeed "improve productivity", but the "because" you provide is ***TOTALLY OFF***.  The "improved productivity" that comes with "writing clear code" manifests itself only on a timescale of the entire ultimate lifetime of the code being written.  "Writing clear code" "improves productivity" ***once that code is getting subjected to the process of keeping it alive and running, i.e. maintenance, i.e. something the "author" is typically ***very much not concerned with*** (at least personally and within the timeframe of the project wihin which said author is doing the writing).

And besides, what will or will not be "clear" to any subsequent reader other than the original author himself, is itself a variable that is just as well dependent on said subsequent readers's ability to understand.

The entire problem of software creation & maintenance in a nutshell.

Quote from Erwin on April 23, 2021, 7:07 pm
Quote from Dave Voorhis on April 23, 2021, 10:48 am

A common myth is that writing clearly gets in the way of writing productively.

It doesn't. Writing clear code improves productivity, because the first reader is the author.

The "simplest thing that can possibly work" refers to algorithmic and structural simplicity. It doesn't mean the fewest keystrokes, single-letter variable names, monolithic functions, and unreadably-chained eye-watering expressions.

I beg to annotate.

"Writing productively" clearly refers ***exclusively*** to the process of writing, i.e. the process of creating new code.  And yes, in that particular narrow view of "writing", "writing clearly" absolutely and undoubtedly does get in the way of "productivity".

No, it doesn't. That's a common myth.

Try writing code as clearly as possible to begin with. That doesn't mean it will start out as clear as it will become after multiple passes and refactoring, but the effort to be clear from the outset has a beneficial effect.

Try it. I bet you'll find your productivity, rather than decreasing because you don't "get it all down" or whatever, increases.

Possibly significantly.

That doesn't mean there won't be bursts of flow where you do have to get it all down, in which you inevitably look back later and see much opportunity for improved clarity, but striving for clarity to begin with -- rather than relying on refactoring to do it (not that you should stop refactoring; far from it) -- will almost certainly result in better code and better productivity overall.

Try it.

I'm the forum administrator and lead developer of Rel. Email me at dave@armchair.mb.ca with the Subject 'TTM Forum'. Download Rel from https://reldb.org
Quote from Erwin on April 23, 2021, 7:07 pm
Quote from Dave Voorhis on April 23, 2021, 10:48 am

A common myth is that writing clearly gets in the way of writing productively.

It doesn't. Writing clear code improves productivity, because the first reader is the author.

The "simplest thing that can possibly work" refers to algorithmic and structural simplicity. It doesn't mean the fewest keystrokes, single-letter variable names, monolithic functions, and unreadably-chained eye-watering expressions.

I beg to annotate.

"Writing productively" clearly refers ***exclusively*** to the process of writing, i.e. the process of creating new code.  And yes, in that particular narrow view of "writing", "writing clearly" absolutely and undoubtedly does get in the way of "productivity".  The loss of productivity that comes along with "not writing clearly" is incurred only later.

And so yes, "writing clear code" does indeed "improve productivity", but the "because" you provide is ***TOTALLY OFF***.  The "improved productivity" that comes with "writing clear code" manifests itself only on a timescale of the entire ultimate lifetime of the code being written.  "Writing clear code" "improves productivity" ***once that code is getting subjected to the process of keeping it alive and running, i.e. maintenance, i.e. something the "author" is typically ***very much not concerned with*** (at least personally and within the timeframe of the project wihin which said author is doing the writing).

And besides, what will or will not be "clear" to any subsequent reader other than the original author himself, is itself a variable that is just as well dependent on said subsequent readers's ability to understand.

The entire problem of software creation & maintenance in a nutshell.

I agree. But I think you missed my point. There are 3 'readers': (1) me while I'm working on the code, (2) me 12 months from now and (3) anyone else anytime. The most productive way to write is short and fast, and only reader (1) needs to read my first attempt at a solution. Reader (2) has forgotten everything but knows my style, so I need to refactor private code to leave it in that condition. Reader (3) is a stranger and perhaps less skilled, so the code has to be top quality. Much of this refactoring happens during testing, as unit tests uncover weaknesses in the original code.

Fred Books said "plan to throw one away, you will anyway". I treat all first attempts as a prototype, the 'simplest thing that can possibly work', and I want super short. It's only the code I leave after refactoring that has to 'good enough', and  usually longer.

Andl - A New Database Language - andl.org

But you refuse to allow the compiler to do meta-programming, and prefer to have external code generation and runtime reflection.

I refuse to embrace macros, and metaprogramming almost invariably reflects fundamental language limitations. Fix those, and you don't need it.

Fortunately others realise that metaprogramming exists precisely to address those limitations that the techniques you find to some comforting cannot address.

I don't prefer code generation or runtime reflection. I endeavour to avoid both. Occasionally, I accept that either or both are preferable to the alternatives, which are usually manually writing code or macros.

Macros are an abomination.

The true abomination is hiding stuff from the compiler and leaving the mess to be sorted out at runtime. Things like reflection and annotations targeting runtime code guarantee that bugs will be later and more expensive than anything you could have done at compile-time.

 

Andl - A New Database Language - andl.org
Quote from dandl on April 24, 2021, 12:20 am

But you refuse to allow the compiler to do meta-programming, and prefer to have external code generation and runtime reflection.

I refuse to embrace macros, and metaprogramming almost invariably reflects fundamental language limitations. Fix those, and you don't need it.

Fortunately others realise that metaprogramming exists precisely to address those limitations that the techniques you find to some comforting cannot address.

What "others realise" doesn't matter to me. Metaprogramming in general refers to treating programs as data, which includes facilities like reflection, self-modification, and various forms of macros, of which only reflection is relatively safe. The other two all-too-often and all-too-easily lead to code that is complicated, obfuscated, obscure, and without appropriate compiler safety-nets, potentially unsafe.

But that's not the crucial point, which is that (in the case of macros, at least) they almost invariably reflect fundamental limitations of the language.

A common limitation that encourages use of macros: being unable to parametrically reference arbitrary variables.

Another: being unable to return/emit some parametrically-defined and statically-checked arbitrary construct like a class or block of structured code.

Fix those, and the apparent need for macros goes away, subsumed into the reasonable (easy-to-reason-about, unlike macros) function/procedure/method mechanism. But it does mean rethinking what is first-class (ideally everything) and what isn't (as little as possible.)  Smalltalk is a notable example of this.

A different approach is to provide such a seamlessly integrated macro facility that the usual macro/language impedance mismatches (which are at the root of making it abominable) are reduced. The Lisp family is a notable example of this, but it's perhaps still a bit too easy create a morass of undesirably "meta" incomprehensibility.

I don't prefer code generation or runtime reflection. I endeavour to avoid both. Occasionally, I accept that either or both are preferable to the alternatives, which are usually manually writing code or macros.

Macros are an abomination.

The true abomination is hiding stuff from the compiler and leaving the mess to be sorted out at runtime. Things like reflection and annotations targeting runtime code guarantee that bugs will be later and more expensive than anything you could have done at compile-time.

I agree that runtime failure needs to be minimised. Reflection, like macros, are a mechanism to work around fundamental language limitations. Those limitations are what need to be addressed.

What language are you thinking of where annotations target runtime code?

In Java, annotations are primarily a compile-time mechanism, but a given annotation type can be optionally instructed to set runtime metadata so it can be introspected at runtime.

I don't think much of annotations, either.

I have more tolerance for external compilation/transpilation -- specification in language x goes in, code in language y comes out -- than bodgy in-language mechanisms like macros. That does mean the overhead of a compiler/transpiler to maintain, but at least it's decoupled and standalone. And, again, ideally whatever the external compiler/transpiler does should be something we can do in-language with programming (and have it statically checked) rather than metaprogramming.

However, I do recognise the difficulty in achieving that, particularly when we want to have statically-verified complex constructs appear at runtime as a result of something that's happened at runtime, like retrieving a structured value from some external service where we can't know the structure until we retrieve it.

My current approach to such things (in Java, at least) is (for some purposes) to receive the value, invoke a transpiler that converts it to Java source, invoke the Java compiler to generate Java binaries from the Java source, then use reflection (plus a bit of horror called a custom ClassLoader) to use the generated Java at runtime. Note that this is entirely in code, running within the Java program. No human interaction is involved.

Or (for other purposes), invoke a transpiler that converts the value to Java source, then integrate the generated Java source into the current project. There may be some negligible human involvement to launch the transpiler and invoke 'Refresh' in the IDE afterward.

Neither of these are ideal, but the alternatives (e.g., use a dynamically-typed language!) are worse, and macros wouldn't help.

I'm the forum administrator and lead developer of Rel. Email me at dave@armchair.mb.ca with the Subject 'TTM Forum'. Download Rel from https://reldb.org
Quote from dandl on April 24, 2021, 12:08 am
Quote from Erwin on April 23, 2021, 7:07 pm
Quote from Dave Voorhis on April 23, 2021, 10:48 am

A common myth is that writing clearly gets in the way of writing productively.

It doesn't. Writing clear code improves productivity, because the first reader is the author.

The "simplest thing that can possibly work" refers to algorithmic and structural simplicity. It doesn't mean the fewest keystrokes, single-letter variable names, monolithic functions, and unreadably-chained eye-watering expressions.

I beg to annotate.

"Writing productively" clearly refers ***exclusively*** to the process of writing, i.e. the process of creating new code.  And yes, in that particular narrow view of "writing", "writing clearly" absolutely and undoubtedly does get in the way of "productivity".  The loss of productivity that comes along with "not writing clearly" is incurred only later.

And so yes, "writing clear code" does indeed "improve productivity", but the "because" you provide is ***TOTALLY OFF***.  The "improved productivity" that comes with "writing clear code" manifests itself only on a timescale of the entire ultimate lifetime of the code being written.  "Writing clear code" "improves productivity" ***once that code is getting subjected to the process of keeping it alive and running, i.e. maintenance, i.e. something the "author" is typically ***very much not concerned with*** (at least personally and within the timeframe of the project wihin which said author is doing the writing).

And besides, what will or will not be "clear" to any subsequent reader other than the original author himself, is itself a variable that is just as well dependent on said subsequent readers's ability to understand.

The entire problem of software creation & maintenance in a nutshell.

I agree. But I think you missed my point. There are 3 'readers': (1) me while I'm working on the code, (2) me 12 months from now and (3) anyone else anytime. The most productive way to write is short and fast, and only reader (1) needs to read my first attempt at a solution. Reader (2) has forgotten everything but knows my style, so I need to refactor private code to leave it in that condition. Reader (3) is a stranger and perhaps less skilled, so the code has to be top quality. Much of this refactoring happens during testing, as unit tests uncover weaknesses in the original code.

Fred Books said "plan to throw one away, you will anyway". I treat all first attempts as a prototype, the 'simplest thing that can possibly work', and I want super short. It's only the code I leave after refactoring that has to 'good enough', and  usually longer.

There's an unfortunate tendency in the industry to never get around to refactoring or even openly deprecate it ("I told you before, we don't have time for that 'Agile' crap around here, Voorhis!") and push such prototypes into production.

I'm the forum administrator and lead developer of Rel. Email me at dave@armchair.mb.ca with the Subject 'TTM Forum'. Download Rel from https://reldb.org
PreviousPage 8 of 11Next