"Good Syntax" is often heavily subjective. So it makes sense to allow people to program in the styles they like best. I would argue that there is also value in the ability to lock-up and restrict syntax features (for things like compile-to-OpenCL, or game scripts), but that's beyond the scope of this document.
Many programmers prefer empirical style to functional. In the order to attract these people to Nimrod, and to give all developers freedom of expression, I have a few ideas on how to change and extend Nimrod to support a more empirical coding style, in addition to it's already great functional style.
First, a gripe. I don't like the inconsistency of type
syntax. No other object is defined this way in the syntax. The following:
type Foo = object
...
type Bar = object of Foo
...
would be best if expressed similar to 'proc's style:
type Foo =
...
type Bar: Foo =
...
Alternatively, as Araq said in a recent conversation, the following short-hand syntax would accomplish the same thing:
object Foo =
...
object Bar of Foo =
...
Just as function proc foo(a:bar)
can be called as both foo(b)
and b.foo()
due to UFCS, I think it makes sense to also allow the definition to appear as both functional, and empirical, to fit the programmers preference. For example, the following two functions would be identical:
proc bar(this:Foo) =
echo this.baz
proc Foo.bar() =
echo this.baz
Just as result
is automatically defined within procs which return a type, this
could be automatically defined as an parameters for procs which are defined with a type prefix.
Inits would be a special type of proc, which are callable from both the Type reference (in which case they act as factories), or by an instance (in which case they act as initializers).
Similar to above UFCS example, init
's would auto-define a this
parameter as the type either prefix if declared using empirical style, only as the return type rather than a parameter (alternative would be to use result
). For example, the following two inits would be identical:
init new(name:string): Person =
this.name = name
init Person.new(name:string) =
this.name = name
The following is a quick example of the features combined.
# Person
type Person =
name: string
age: int
init Person.new(name:string, age:int) =
this.name = name
this.age = age
proc Person.greet(name:string, age:int) =
echo "Hi, my name is ", this.name, "."
echo "I am ", this.age, " years old."
# Employee
type Employee: Person =
salary: float
init Employee.new(name:string,, age:int, salary:float) =
this.new(name, age)
this.salary = salary
proc Employee.greet() =
(this as Person).greet()
echo "I make ", this.salary, " per year."
# main
block main =
var bob = Person.new("Bob", 28)
var joe = Employee.new("Joe", 26, 25_000)
bob.greet()
joe.greet()
type Image =
width: int
height: int
init Image.new(width, height:int) =
this.width = width
this.height = height
init Image.load(path:string) =
var bitmap = Bitmap.load(path)
this.width = bitmap.width
this.height = bitmap.height
block main =
var image = Image.new(512, 512)
canvas.drawImage(image)
image.load("some/image.png")
canvas.drawImage(image)