These are some syntax ideas for a programming language bouncing around in my head which I'll never get around to making because honestly it's not really necessary but it's fun to think about nonetheless.
This language's syntax is based largely on Objective-C but would be designed in a more modern way. No primitives, everything is an Object.
Resembles Objective-C quite a bit, but tries to simplify things when possible. Semicolons aren't needed. Comments are the same as in Objective-C/C. Most control statements are the same, too (if
, for
, etc.).
Classes are defined in a single file (no headers). An example class definition:
@implementation Person : Object
@property occupation
@property (read) name // options are (read or write), default is write and can be omitted
// private instance method called `sayHi` which takes no arguments and returns void
- sayHi {
[[self name] log]
}
// public instance method called `sayHiTo:` which takes 1 argument and returns void
// methods can be (private, protected, or public), default is private which can be omitted
- (public)sayHiTo:otherPerson {
["Hi to " + [otherPerson name] log]
}
// public class method called `betterNumberOne:orNumberTwo:` which takes 2 arguments
// and returns an object
+ (public)betterNumberOne:one orNumberTwo:two {
return two
}
@end
Classes start with the @implementation
keyword followed by the class name to be implemented. It is then followed by a : SuperClass
. Object
is the default and therefor can be omitted.
(Note the use of @-keywords
might be dropped. For now I like them but they might end up being kind of silly in the long run. I'm not married to them)
A @property
is a declaration of an instance variable for a class and can have several options, as seen below:
@property bias // read/write, private, instance property. Default, so no options specified.
@property (public) name // read/write, public, instance property.
@property (public, read) age // read-only, public, instance property.
@property (class) stork // read/write, private, class property.
The options for a property are as follows:
- One of
private
,public
, orprotected
. Defaults toprivate
. (Note: not sure ofprotected
) - One of
write
orread
. Defaults towrite
. - One of
instance
orclass
. Defaults toinstance
.
A property has a matching instance variable (or class variable) that is the property name prefixed with an underscore. So, @property name
has an ivar called _name
.
A property automatically has methods generated for it, depending on its read/write settings. In the default case, for @property name
this will generate -name
and -setName:
, two private, instance methods. For @property (public, read) age
that would give - (public)age
.
The reason why properties are private
by default is the same as why methods are private
as default: It's because you typically have a lot more internal details than external. That is, the language should encourage good interfaces. Put another way, be liberal in your private implementations and be stringent in your public API.
An @interface
is not the same as an Objective-C @interface
, but instead a lot more like Go's interface
keyword (or like Java's). It's a set of methods, either required or optional, that a class must implement to be conforming. This is like Objective-C's @protocol
.
A class is said to implement an interface if it has an implementation for all its required methods. It does not need to declare that it implements the interface (just like in Go).
For example:
@interface Writer
- (public)writeToFile:file
@optional
- (public)writeToZipFile:zipFile
@end
Interface methods default to being @required
, which can be omitted, or they can be @optional
which is explicitly shown. Methods can also be public
, protected
or private
, with the default being private
. Private interface methods typically don't make as much sense, so usually you'll want to explicitly state public
. Maybe these ought to default to protected
instead, so at least subclasses can override them. These only make sense if it would also be possible for a consuming class to be able to tell what the conforming class implements. Given the interface
interface Writer
- (private)doSomethingPrivate
end
Would it make sense for that method to be private
? If it were private, how does an expecting class know whether or not the implementor conforms? It feels like interface
methods should just all be public
.
There are three kinds of messages: unary
, binary
, and keyword
messages.
unary
messages are sent to the receiver with no arguments for messages like++
. e.g.,index++
.binary
messages are sent with one argument for messages like+
. e.g.,index + 1
.keyword
messages are sent with n >= 0 arguments for messages like-add:
. e.g.,[index add:1]
.
-
The language should encourage good program design.
-
The language should get out of the way for its defaults and not make the programmer type needlessly.
-
The language should have sensible, opinionated defaults.
-
The language should do the obvious thing.
-
The language should make simple things easy and complex things possible.
-
Identity is derived from ability. It's more important what you can do than who you are.
-
Development of tools should be simpler.
-
A programmer should see changes as quickly as possible. The language should encourage play. (REPL)
A project is a directory that meets the following conditions:
- Is under source control (e.g., git, hg, etc.)
- Has a
project.json
file - Has a
readme
file of some kind
project.json
should be a standard json file which gives some info about the project. It shouldn't be like an Xcode project file, as IDEs should just mimic the directory structure of the project directory.
But it might look something like this:
{
project: {
deps: [
"image-tools", "http://github.com/i/image-tools.git",
...],
},
custom: { ... }
}
Binary (and unary) messages should allow for somewhat simplified syntax for common operations. One of the most common things a developer will do is manipulate strings, arrays, and dictionaries. By allowing some binary messages, this simplifies their usage.
<< // Give method. e.g., Log << "some string to print out"
>> // Get method. e.g., Console >> inputString
Examples
Log << someString
array << objectToAdd
secondArray = Array << otherObject
For working with dictionaries or anything else requiring strings for keys, there is a special syntax for quickly declaring a key:
dictionary[@key] = value
[someObject setValue:value forKey:@key2]
This is a lot like Ruby's :symbol
notation, except using the @
symbol instead of :
.
The more I think about not using @-keywords the more it seems like a good idea. They were used in Objective C to avoid conflict with C. They were used in Objective J to avoid conflict with Javascript. In this language's case, there is no existing language which we're bolting on to, so it would be silly to use @-keywords. A revised look at what a class would look like:
implementation Cat : Animal // implementation or "class"?
property (public, read) name // read-only, public instance property
- (public)newCatWithName:name {
[self setName:name] // Should a private setter always exist?
}
end
The language should have string interpolation like as is done in Ruby. Allow expressions to be evaluated inside of string literals like such:
myString = "My name is #{[self name]}."
A Boolean
is what is used to represent truthiness in the language and can either have the value yes
or no
. Because everything, including numbers, are objects, the only values which represent false are no
and nil
. Everything else, including 0
is considered to be true.
In C-like languages, the logical And/Or operators are &&
and ||
respectively. But in this language, the operators will be known as and
and or
respectively. This just makes the language easier to read.
Arrays and Dictionaries have special syntax because they're so frequently used:
myArray = [object1, someString, 19, yes]
myDictionary = {
@name: "Jason Brennan",
@age: 24,
someOtherKey: value
}
A fundamental feature of the language should allow for it to be composed of many smaller sub-projects or libraries. Many projects these days depend on open source libraries and the language should support and encourage this. It should encourage the use of self-contained groups of objects.
I was thinking of doing a better kind of symbol importing for this language, one that would facilitate tools like Eclipse's auto-import of required classes, or removal of them. But the more I think about this, if it's possible for an IDE to figure out which libraries are needed to be imported, then why bother writing import statements at all? If a tool can figure out which other classes are needed, then it should happen automatically without the need for the user to do it. So maybe there should be no import
statements at all.