Fundamentals
- No primitive types, everything is an Object.
- Strong typing
- No optional syntax: semicolons and parenthesis required
- Has properties
- No facility for statics
- Executable/compiled documentation as much as possible
- Classes have retained metadata
- There is always a seam between any pair of classes, for testing
Readability
- Always be consistent
- Don't use shortcuts or syntax sugar like underscores-for-variables or leaving off parentheses on method calls. These make it quicker to write, but harder to read, especially for a code reviewer who is not expert in the language but knows C++ or Java well.
Good stdlib
- Pick best implementations from other languages
- Use JodaTime for Date/Time apis
- Use util.concurrent for concurrency
- Expose Google collections
- allow concurrent collection iteration when given a ConcurrentIterator?
- Introspection done objective-c style (easy, few lines of boilerplate)
Injection
- Use Guice or PicoContainer under the covers
- Need to think hard about construction - can we use PicoContainer's greediest constructor? Is there always a default constructor?
- Instead of greediest constructor approach, use optional injectables?
- Need to examine scopes carefully. Guice scopes can't be concentric without a lot of work. Pico just uses parent containers/injectors for scope. Guice can work that way... how do we want to do this?
- A type is either newable or injectable, newable types cannot have injectable deps? See ProposalForNewableVsInjectable.
- A first-class binding operator: Service -> ServiceImpl
- Injectors/scopes as first-class language constructs
Immutability
- final is the default, use the mutable keyword to make a variable which may change its reference.
- implement const?
- encourage functional style by having built-in predicate, filter, etc on collections, as well as convenient functions/blocks/closures/lambdas
Strong inferred typing
- Avoid null, Types shouldn't allow null value by default
- Maybe Option<T> to create a nullable T, like Scala. See NullObjectPatternProposal.
- Inferred types? foo = "bar" means foo is a String
Exceptions
- Only unchecked exceptions
- Maybe a way to indicate errors to the caller aside from unchecked exception, see ProposalForErrors.
Class parameters and properties
Testing
- test keyword allows compact syntax for declaring nested test suites with tests as the leaves. No need to use classes and methods to declare test suites and tests. See ProposalForTestingApi.
- Allow test blocks within production code, so the tests are right next to the code-under-test. Probably want to strip that from non-debug/non-test compiles.
- tests have some "friend" relationship with the class under test, to allow whitebox test of private methods
- Every instantiation goes through the injector, so there's always a seam between every pair of classes
Public API
- need some way to enforce public API separate from visibility of types and methods. Maybe don't need "private" at all
- Android has an interesting approach: create interface jars which have the implementations stripped from public classes and don't contain private stuff at all.
Literals
Lots of useful literals!
- String is a type literal
- "Hello" is a string literal
- """line1\nline2""" is a multi-line string literal
- /.*/ is a pattern literal
- { "a": "b", "c": "d" } is a hash literal (should evaluate to most specific type possible)
- [1,2] is an array literal
- `http://google.com` is a URI literal (like Fan, is it useful?)
Optional to separate bindings from code
Can we provide bindings to the injection framework inline, using scopes and @ImplementedBy and such? See PropositionForScopes.
Enforce naming conventions
This can help to improve readability, although we should take care to only enforce widely-agreed-upon conventions.
- No reason the parser should allow types to start with lower case or variables to start with upper case
Thoughts - I think we can avoid bindings where there are obvious singletons (or scopeltons, as I like to call 1-per-scope). Because the compiler/runtime can infer the type to be injected. However, there are problems when you get into the world of testing, or multiple implementations. Modules allow one to wire one way, or another in a different context, without burdening the interface and implementation(s) with pre-knowledge of how they will be bound. If we do that, we're not delegating wiring, and we miss the point. We would sacrifice re-use, etc. We do need a clean way of expressing it, but something has to replace modules, and it shouldn't be expressed with in any of the classes/interfaces to be bound.
In the previous comment, I think you're objecting to something like @ImplementedBy?? I agree that we must be able to delegate the wiring, but can't this be treated as an overridable default?
As for checked vs. unchecked exceptions, I really don't like checked exceptions. I can go into it, unless there's already consensus :)
I'm all for unchecked exceptions. And I am concerend with @ImplementedBy?. I can live with that as the default, but I'd rather have it not be there at all, as it creates knowledge by the interface of an implementation, and I think that's evil. That's the wrong direction for a dependency. Wrong wrong wrong. To me that's the job of the binder, if it find ambiguity. (I'm not even really happy with declaring scope on the interface or implementation, as it ties the use of the thing to its definition, when it could be used in different contexts. I'd rather declare that at binding time, and if there is no specific binding, it should either default to singleton, or default to "prototype" (instantiated once per request from the container)
How would we feel about "and" and "or" (python style) instead of && and || ?
For readability.
I personally like "and" and "or".
I was thinking, part of readability is that even people who aren't very familiar with a language should be able to do code reviews at least - so the notion of readability should be as universal as possible. That's why leaving parentheses off method calls in Scala is trouble - if you don't know Scala, it's hard to read that a method is being called.
Following that logic, most coders will be used to reading "&&" and "||" from other languages they know, so not sure we can declare "and" and "or" to be more readable.
I prefer it, but do we then have xor, nor, xnor, nand?
Yeah, perhaps.. I personally find it more readable and I think there is good precedence for it in python and ruby, but totally understand the other view... As far as xor, nand, etc, if we were to have "and" and "or" and if we aren't afraid to add keywords, why not? :)
On an sort of related note, I always liked being able to give the conditional at the end:
This gets to ternary expressions, which can be more readable this way as well...
Otherwise I guess we would use (a ? b : c) ?
For most of the philosophy, fundamentals, readability, and API issues, wouldn't something akin to Smalltalk's grammar be one of the simplest and most powerful general solutions?
I know this is one of those horrid sniper's off-the-cuff comments, but I wonder if your energies are best focused on only fixing the broken wheels, rather than re-inventing all of them.
It would be nice if noop had some nice way to define templates for things like sql,html,xml,display layouts, which programmers are forced to use all the time. The vast majority of programming languages are pretty awful at making this code readable.
Where can i see some example code!!??
not lexing the tab character only makes sense if whitespace is not whitespace, but syntactically meaningful, which it shouldn't be. If scoping is bracket-based (by using accolades) then the parser should be able to tokenise on /\s+/. Rejecting tabs due to a designers fondness of precise code indentation by using spaces would be baking personal preference where it has no business.
under Strong Inferred Types: Inferred types? foo = "bar" means foo is a String
please no - this harms readability - a LOT. instead, provide generic cast operation. this does not help the string case (which is not the problem), but helps in the much more common case of reduced readability through hard to read cast notation. sort of the other side of the coin.
SomeClass? bar; ... SomeOtherClass? foo := bar; // := means cast to my type or a superclass
or just do it on a regular assignment - assuming that casts are always happening when direct subtypes/interfaces do not match up.
the point is that the type for the destination should always be explicit, so you know what you end up with.
Please... please don't have checked exceptions. They are worse than useless. If you want to catch a specific exception with confidence in java, you have to have code that catches Exception and then unwraps it to find the specific exception it is looking for. This in essence reduces exceptions to an untyped system when looking for a specific exception type.
It results in implementation dependent brittle code. Look at the horror in java where methods and interfaces have to be either "remote" or "local" because remote methods have their exceptions wrapped in RemoteException?. Java reflection has similar exception wrapping issues. When you are compelled to reveal implementation details in an interface, something is wrong. Encapsulation 101 says the client should not know or care how the interface is implemented.
Exceptions by their very nature are implementation dependent. A throws clause should be nothing more than a documentation feature to inform calling code of exceptions that are thrown by the logical design. You don't want to have to wrap implementation specific exceptions. Either a method can handle an exception or it can't. If it can't, the exception should pass through to a higher layer. Any imposition of checked exceptions will result in the brittle implementation sensitive designs that are typical in Java.
@Thoughts - I think we can avoid bindings where there are obvious singletons (or scopeltons, as I like to call 1-per-scope). Because the compiler/runtime can infer the type to be injected.
What? Singleton implementations are SUPPOSED to be dynamically substitutible. Just because most developers/designers don't do this, doesn't mean you should hardwire it as such. It's not just "scope". The implement class can be different, thanks to polymorphism. Am I missing your point?
I like the gabrielh's vote to put the conditional at the end:
I'd also like to suggest my favorite looping construct from Pick Basic (yes Basic):
loop { x = doSomething(); } while (!x) { doSomethingElse(); }putting the test in the middle of the loop allows you to dispense with any setup code for the loop that has to be repeated within the loop -- it all goes before the test and will be executed again for each iteration.
I, for one, hate Python's triple-""" business, and far prefer a simple system of ".." only with \ attributed magic powers in the middle (so "\"", "\n", etc).
Also, where does that awful backtick for URIs come from, when there are existing uses of <> for the purpose?
For hash literals, what would be the hash type? <Object, Object>? Or would it be inferred from the tightest type that allowed all of the <keys, values>? Or would it be the types of the first key, value pair?
I'm guessing <Object, Object>.
Please get rid of semicolons, they are the most useless piece in a programming language :(
An array is essentially a function that takes a numeric parameter and returns a value. A Map can also be viewed as a function that takes an object (usually a String) argument and returns an object. They are essentially parametrized objects. Why can't we unify the syntax?
A template (Generics) takes a parameter and behaves like a function also. Can we then move toward a syntax similar to the following?
Array(Int) factorial = {1, 1, 2, 6, 24, 120}; Int fourth = factorial(3); // fourth == 6 Object myObj = myMap("myKey");We can standardize this feature for all classes by using a special method:
class TableRow() { String get(String name) { /* Return field name as a String */ } String get(Int i ) { /* Return field i as a String */ } } TableRow row = getNextTableRow(); String city = row(5); // city == "Alexandria" String country = row("Country"); // country == "Egypt"This is especially useful if we can extend the syntax to set values and not only retrieve them. The syntax might then look like this:
class TableRow() { String get(Int i) { /* Return field i as a String */ } void set(String value, Int key) { /* The first parameter is the new value */ } } TableRow row = getNextTableRow(); String city = row(5); // city == "Alexandria" row(5) = "Ankara"; // row(5) is mutable of course String town = row(5); // town == "Ankara"What you need is a strong dose of LISP. Forget dependency injection and such stuff. What you need is compile-time evaluation.
What is the background for omitting facilities for static code? I would love to see static fields go away, but static methods are mighty useful for setup/facade code. Why should it take object instantiation to do Integer.parseInt(..) ?
While <uri> would be better than `uri`, I have to suppose it conflicts with the relational operators.
What would be even better is no literal markup at all. Have a scheme for registering protocol names (like SPARQL & Adenine @prefix) and then these are URI literals:
And +++1 for compile-time (and every other phase) evaluation.
@jeppe.b.smed: It might not take object instantiation. I imagine we could create a standard facility for parsing Ints from Strings in an IntegerParser interface, which you would obtain like so:
class MyClass(IntegerParser parser, ...) { ... private void myHelperFunction() { String input = ...; // Do some work Int parsed = parser.parseInt(input); // Calculate something with parsed // Change internal state based on above calculation } }By default your injector could have a binding:
In your tests you could replace the binding with DumbIntegerParser? which simply returns 5 or some other suitable value.
well
"Static methods" can go away if First Class Functions are added to the language. good or bad? I don't now.
Checked exceptions are fine as long as they do not appear within the core language APIs. No checked SQL, IO, Security, etc. Checked exceptions are best used at the Domain level only. Allowing the language to document it's exceptions via a throws keyword is good.
I do not like its name, why noop? no operation? And Scala and java, JRuby would take all the users~
How about making concurrency a first-class citizen? In our multi-core future we would all benefit from language constructs that make multi-threaded programming as simple as possible. Java 7's Fork/Join and util.concurrent are nice but it would be great to have some higher-level language constructs for concurrency.
I hope you are concidering the for...else construct, which is very useful when searching:
for item in list: if isWhatWeAreLookingFor(item): break else: print "item not fount"One common mistake I happen to see a lot is equality check for floating point numbers, i.e
float a = 0.1f; float b = 0; for(int i = 0; i < 10; i++){ b += a; } System.out.println(b == 1);depending of float implementation, (b == 1) could be true or false. In java is indeed false, and this is counter intuitive for young programmers. I think simply don't allow equality checks for floating point or even including an equal-with-tolerance operator aprox(a, b, 0.001) in the language itself could be more than useful.
switch ( expression ) { case value1, value2 : { instructions } case value3, value4 : instruction; case value5 : { instructions } default : { instructions } } // switchchecks if expression.equals(valueX) because everything is Object
specially useful with String :
switch( command.toLowerCase() ) { case "cp","copy" : { /* do the copy */ } case "mv","move" : { /* do the move */ } default : { System.out.println( "Unknown command : " + command ); } } // switchespecially for classes, methods, and program structures (not for blocks); it greatly improves readability and debugging.
Let's not ban float a, b; a==b... or double a, b; a==b... instead, let's make it easy to define what it means for the programmer: float.setEpsilon(1E-4); float a, b; floatsEqual = (a == b); double.setEpsilon(1E-10) double x, y; boolean doublesEqual = (x == y); also, please don't even consider multi-word types (e.g. long double, unsigned char).
Also, along the lines of switch, as long as we're extending it, we can extend it even more:
switch (variable) { case 4, 5, 6, is > 1000, in someCollection: { someCollection.remove(variable); } case in someOtherCollection: { performOperation(variable, someOtherCollection); break;} default: { System.out.println("Don't know what to do with " + variable); } }
That didn't get formatted right... trying again:
switch (variable) { case 4, 5, 6, is > 1000, in someCollection: {someCollection.remove(variable); } case in someOtherCollection: { performOperation(variable, someOtherCollection); break;} else: { System.out.println("Don't know what to do with " + variable); } }Please also note that I changed default: to else:.
Instead of
for item in list: if isWhatWeAreLookingFor(item): break else: print "item not fount"I could simply write something like:
var tmp = list.first(isWhatWeAreLookingFor); if (tmp == null) print("item not fount");which is shorter, easier to read and more explicitly tells what you want (you want the first item that matches your criterion, not a bunch of implementation details).
Don't use "mutable", use "var" as the keyword for mutable references.
Why not get rid of Constructors named after the class... do it better like Ruby. Refactoring is simplified when the class just has "initialize"... Please at least allow it.