The current threads seem to be about pretty specific things so I'm making this general topic, feel free to add your own ideas or tell me that mine are stupid.
Note: All suggestions with a # before them are features I don't think are essential for the first release and can be later added without breakng backward compatibility
Note: Although my examples all use the same case, this would work fine without case-sensitivity so I don't mind either way
1 - Naming conventions
1a - All functions whose names contain some American/British English variants should be replicated so a variant for each exists
1b - Functions no longer contain the $ symbol when returning strings, i.e. Str$()
2 - General language syntax
2a - All function calls should require brackets
#2b - Add support for references
2c - Array creation and access uses square brackets []
3 - Language features
3a - No 'gosub' call
3b - There should be way more data types
3b-1 - 'Function' should be a data type
3b-2 - 'Array' should be a data type
3b-3 - 'Dictionary' should be a data type
3b-4 - 'Image/Sprite/Sound/etc' should be a data types
3b-5 - 'Float2/Float3/Float4/Float4x4/Float3x3/etc' should be data types
3b-5a - The above vector types should have basic operators defined, such as multiply
3b-6 - Allow all the above types to be declared in a UDT
#3c - Allow retrieval of function objects via not using brackets to call it
3d - Variables in UDTs should be allowed to have default values
#3e - Allow functions to be declared inside functions with access to outside variables, aka lambdas
#3f - Some type of namespace support, maybe allow the user to import a library
3g - Support for common comment characters/blocks, i.e. // /* */ rem ` remstart remend
3h - Arrays should have 1 based indices
3i - Better scopes; each control structure is a deeper scope level
3j - Variables can be created via assignment, i.e. 'myString = "Apples"' or 'for i = 1 to 10'
3k - Basic types such as String/Integer should have constructors that can take other types as arguments to perform conversion
3l - Print should accept all data types, including UDT types
4 - Library features
4a - All functions that create a resource(like an image) should return the associated resource data type rather than require an index
#4b - The string library should contain functions to deal with UTF-8 input
#4c - The file functions should treat the passed paths as UTF-8
Justifications
1a - I hate writing color
1b - This is a static language, so binding the type to the function name is pointless and forces the user to learn more syntax rules
2a - Consistency is a good thing
2b - I cringe every time I see DBPro code with something like: myArray(indexTo).something = myArray(indexFrom).something, except that line is done about 50 times for each type, and it can't be sped up. This feature can easily be added at a later date without breaking existing code
2c - Using round brackets for everything doesn't make much sense, 'var = myArray( 5 )' looks identical to a function call, so differentiating them will increase readability
3a - It's pointless and makes the language more complex for no reason
3b-1 - This allows for elegant callbacks and keeps everything consistent
3b-2 - Why not? Dim makes no sense in DBPro, it's so drastically different in terms of syntax from everything else when it shouldn't be
3b-3 - This would map between strings and some other data type, similar to arrays, which map between integers and some other data type
3b-4 - I don't see the point of the resource index system, passing an integer which is actually a resource index isn't descriptive in the slightest, and it forces you to name your variables with what type they are
3b-5 - Why wasn't this the case in DBPro? Vectors are so fundamental to game programming and their use in DBPro is gimped to say the least. I used the Float(height)[x(width)] syntax because it makes it more clear as to what type is being mapped, and allows for DoubleFloat4x4/Integer4 maybe?
3b-5a - Definiting the basic operators for these vector types makes code very legible, more complex operations such as DotProduct/CrossProduct/Decompose/etc should of course use functions
3b-6 - Makes a lot of sense! And it also automatically allows for arrays within UDTs, because arrays would be a standard data type! As well as functions within UDTs
3c - This ties into 3b-1 and 3b-6 and makes sense, however this isn't amazingly important, and adding this at a later date won't break anything
3d - This should be obvious, e.g:
Type MyType health as Integer = 100 EndType
3e - This powerful feature can be added at a later date without breaking existing code
3f - If we have some type of library support(which we should because NO ONE shared libraries in DBPro, as there was no support for it, and TCPs are a naming convention mess) then maybe the 'import' statement could specify the namespace it gets shoved into?
3g - These are the same as DBPro, except for the C-style block comment, I personally like // because you can stack loads in a row to separate sections of code, the ` grave accent doesn't look as nice!
3h - The 'for i = 1 to count' is quite a nice way of interating through a loop and can be done with a 1 based array, 0 based indices makes sense from a low-level perspective, but isn't this supposed to be a high level language to begin with?
3i - DBPro's 3 levels of scope isn't good enough, any control structure should be able to access any variables declared in any of its parent scopes and nothing else, so no more declaring globals in functions, globals must be outside of functions, so no need for global
3j - DBPro allows you to do stupid things like 'print i + z' which makes no sense, allowing creation via assignment or full declaration makes the most sense and is very clean
3k - This removes the need for functions such as Str$(), Val(), which are very confusing for beginners. Simply having 'age = Integer( "1234" )' is amazingly clear and stays consistent with DBPro's style
3l - This would simplify code a lot, I often have to write 10 lines of code to just print the contents of a UDT
4a - What's the point of the whole index system? Is it just so that the spinning cube example can be a few characters smaller because you don't have to create a variable for the cube object? The index system causes so many problems and is one reason why no one ever shared DBPro libraries, because they were always incompatible. I could rant all day about this.
4b - And by deal with, I just mean a feature to count the amount of glyphs in a string, and various other UTF-8-aware variants of certain functions
4c - This takes little effort to implement and solves a lot of issues with localization
Differences between DBPro and this
These would be the major differences between DBPro and this script, assuming you didn't use all the optional features suggested above
Images in DBPro
Sync On
Load Image "blood.png", 1 // This 1 index isn't descriptive in the slightest
do
for i = 1 to 100
Paste Image 1, rnd(5), rnd(5)
next
Sync
loop
Images with the AppGameKit concept
// SyncOn() - This is redundant I guess? Have it on by default!
blood = LoadImage( "blood.png" ) // LoadImage returns the type Image, which doesn't have to be specified because I'm creating this variable by assignment
do
for i = 1 to 100
PasteImage( blood, rnd(5), rnd(5) )
next
Sync()
loop
Type casting in DBPro
myInt as Integer
myStr as String
myStr = Str$( 1337 ) // This requires the user learn about the Str$ command, as well as the $ symbol and what it means
myInt = Val( myStr ) // Same as above
Type casting with the AppGameKit concept
myInt as Integer
myStr as String
myStr = String( 1337 ) // Casting uses the existing String identifier and acts just like a function that takes an integer and returns a string, easy to learn and remember
myInt = Integer( myStr ) // Same as above
UDTs in DBPro
Type myType
// This is all valid DBPro code, but I think it's really messy
name$
a
b
c#
d as Integer
EndType
// Create a variable using the UDT, and set the default values
variable1 as myType
variable1.name = "Default"
variable1.a = 5.3
variable1.b = 10
variable1.c = 52.7
variable1.d = someVariable // This item would be unique to each instance of the UDT
// Notice all the boiler-plate code I must use to initialize it to a useful state
UDTs with the AppGameKit concept
Type myType
name = "Default" // This automatically gets declared as a string, because I'm assigning one
a = Integer( 5.3 ) // Integer() casts whatever's inside to to an Integer, so a is declared as one
b = 10 // 10 is an integer, so it declares b as one
c = 52.7 // 57.2 is a float, so it declares c as one
d as Integer // We don't specify a default value, so we must specify the type
// The following line would be illegal, because there is no explicit 'as Type' or type-deduction via assignment i.e. 'e = 5'
e
EndType
variable1 as myType // Automatically assigns the default values, way more logical than DBPro
variable1.d = someVariable // This item would be unique to each instance of the UDT
Arrays in DBPro
dim myArray(5) as string // Creates 6 indices, 0-5, makes no sense
myArray(0) = "a"
myArray(1) = "b"
myArray(2) = "c"
myArray(3) = "d"
myArray(4) = "e"
myArray(5) = "f"
for i = 0 to array count( myArray )
print myArray(i) // Looks a lot like a function call
next
Arrays with the AppGameKit concept
myArray as String[5] // Actually creates 5, 1-5, makes sense, the square brackets shows this clearly isn't a function call
myArray[1] = "a" // 1 is the first index, less than 1, or more than the size is invalid
myArray[2] = "b"
myArray[3] = "c"
myArray[4] = "d"
myArray[5] = "e"
for i = 1 to ArrayCount( myArray ) // From 1, not 0, makes more sense if you don't think about the bare metal
Print( myArray[i] ) // It's clear we're accessing an array here
next
Variables in DBPro
age = 100 // Age is an integer as expected
height = 5.7 // height is an integer of 6, not as expected
name = "Bill" // Error
name$ = "Paul" // Works...
food as string = "Apple" // Works, but it's inconsistent with the above
country as string = someFunctionThatReturnsAString() // This doesn't work in DBPro
animal as string
animal = someFunctionThatReturnsAString() // Works on 2 lines...
Variables with the AppGameKit concept
age = 100 // Works as expected
height = 5.7 // Works as expected
name = "Bill" // Works as expected
name$ = "Paul" // Error
food as string = "Apple" // Works, and it's consistent with the above
country = someFunctionThatReturnsAString() // This is now one line and doesn't require explicitly declaring the type, because someFunction() returns a string, so the type can be automatically deduced
animal as string = someFunctionThatReturnsAString() // Also works
Functions in DBPro
Function myFunction1( arg as Integer )
EndFunction arg + 5 // Error, this cannot be done in the EndFunction statement in DBPro
Function myFunction2( arg as MyType )
EndFunction arg // This is invalid DBPro code, cannot return typed variables
Functions with the AppGameKit concept
Function myFunction1( arg )
EndFunction arg + 5 // Works as expected
Function myFunction2( arg as MyType )
EndFunction arg // This would work without issues
Examples
Note: These examples mainly show the optional features that would be possible as a result of the above suggestions, and none break compatibility with simpler code
String casting
name = String( 47 ) // Same as shown in the above examples, this casts 47 to a string and declares name as a string
type MyType
age = 10
gender = "Alien"
endType
myTypedVariable as MyType
myTypeInAString = String( myTypedVariable ) // This would cast my UDT variable into a string
Print( myTypeInAString ) // This would print the string: age = 10, gender = Alien
DBPro version:
name as String
name = Str$( 47 )
type MyType
age as Integer
gender as String
endType
myTypedVariable as MyType
myTypeInAString = Str$( age ) + ", " + gender
Print myTypeInAString
Vector types 1 - (In DBPro this would be about 7000 lines)
position = Float3() // As seen in earlier examples, if a type has brackets after it, it acts just like a function that returns the type, in this case it returns a Float3, so position is automatically defined as this
velocity = Float3( 1, 2, 3 ) // In the earlier examples String() could take Integer and Float values and would return a String, here, Float3 can take 3 values, one for each Float
position = position + velocity * 2.0 // This code is amazingly clear, and the underlying data types are irrelevant if they get the vectors from functions like GetSpritePosition or similar
Print( position ) // Prints out: 2, 4, 6
DBPro version 1:
type Float3
x as Float
y as Float
z as Float
endType
position as Float3
velocity as Float3
velocity.x = 1
velocity.y = 2
velocity.z = 3
position.x = position.x + velocity.x * 2.0
position.y = position.y + velocity.y * 2.0
position.z = position.z + velocity.z * 2.0
Print Str$( position.x ) + ", " + Str$( position.y ) + ", " + Str$( position.z )
DBPro version 2(This is the official DBPro way to do vectors):
ignore = Make Vector3( 1 ) // This command always returns true, look at the source code!
ignore = Make Vector3( 2 )
ignore = Make Vector3( 3 ) // Temp result in here
Set Vector3 2, 1, 2, 3
Copy Vector3 3, 2 // Copy velocity into temp vector
Multiply Vector3 3, 2.0 // Multiply the temp vector by 2.0
Add Vector3 1, 1, 3 // Add the position to the temp vector
Print Str$( X Vector3( 1 ) ) + ", " + Str$( Y Vector3( 1 ) ) + ", " + Str$( Z Vector3( 1 ) )
// Clean up
Delete Vector3 1
Delete Vector3 2
Delete Vector3 3
Vector types 2
leftOne = Float( 5 ) // Casting 5 to a float here, we could also write 'leftOne = 5.0', this way just makes the example cleaner
rightTwo = Float2( 7, 9 ) // Notice the syntax between Float and Float2 can be seamless
my3DVector = Float3( leftOne, rightTwo ) // Notice this constructs a 3D vector from a 1D and 2D one, all the vector types would have constructors that take any combination of types like this
Print( my3DVector.x + my3DVector.y + my3DVector.z ) // Prints: 21, notice the vector types have built-in member access, this makes code very clean to read, and the average user may not even need to know about this
Print( my3DVector.xz ) // Prints: 5, 9, notice I select elements x and z and this outputs a Float2, this is called arbitrary swizzling and is the best thing in shaders since bump mapped sliced bread
Print( my3DVector.yyyz ) // Prints: 7, 7, 7, 9, notice I can get the same elements multiple times, this outputs a Float4
my3DVector.xyz = my3DVector.zyx // This reverses the elements
UDT 1
type MyType
age = 5
height = 10.0
endType
a as MyType // Same as DBPro syntax
a.age = a.age * 5
Print( a ) // Prints: age = 25, height = 10.0
// Another way to declare UDT variables, you'll see why it's useful below
b = MyType()
// Reset a to the default state, this is very useful, because UDTs can have their own default values, in DBPro this feature would be less important
a = MyType()
Print( a ) // Prints: age = 5, height = 10.0
// The following aren't as important, but could be future features
Print( MyType ) // Prints the default state of the type: age = 5, height = 10.0
MyType.age = 10 // Modify the default values!
Print( MyType ) // Prints: age = 10, height = 10.0
Function type 1
Function add_version1( a as Integer, b as Integer ) // Notice I explicitly specify the types, in DBPro you can just type 'a, b', I think this should be disallowed here
EndFunction a + b // This is impossible in DBPro
Function add_version2( a as Integer, b as Integer ) EndFunction a + b // This kind of one-line statement cannot be done in DBPro due to random parsing limitations
// Below are more advanced features, but the above style will of course remain
// This is the same as above, except it uses syntax that's consistent with the other type declarations, because Function is also a type
add_version3 as Function( a as Integer, b as Integer )
EndFunction a + b
// This is also the same as the above, but uses the assignment declaration syntax seen with the other variables, except add_version4 is automatically declared as the Function type
add_version4 = Function( a as Integer, b as Integer )
EndFunction a + b
add_version5 as Function // Notice there is no function definition yet, this identical to 'age as Integer', except using the 'Function' type
Print( add_version5( 1, 2 ) ) // This does nothing, because the function hasn't been assigned
add_version5 = add_version1 // Notice we copy the function over, like a normal variable, because it basically is/should be
Print( add_version5( 1, 2 ) ) // Prints: 3
Function type 2
type Button
name as String
// Define a function in a UDT!
Function click()
PlaySound( "click.wav" ) // <-- This is just a made up function
EndFunction
endType
myButton as Button
myButton.name = "Shoot"
aParticle.click() // Call the function, this syntax is identical to DBPro UDT syntax as in the line above, except using the () to call a function
// Create a new Click function for our button
Function NewClick()
PlaySound( "beep.wav" ) // <-- This is just a made up function
EndFunction
// Modify the button
aParticle.name = "Bleeping button" // Change the name
aParticle.click = NewClick() // Change the click function
aParticle.click() // Call the new function, calls NewClick
Function type 3 - (This is pretty advanced and isn't required early on)
Function add( a as Integer, b as Integer ) EndFunction a + b
// Notice I make a function that calls the other one, using variables I set here
Function addOneAndFive() add( 1, 5 ) EndFunction
addOneAndFive() // Call it!
Function returnFiveAndNine()
EndFunction add( 5, 9 ) // This now returns the result of the add
Print( returnFiveAndNine() ) // Prints: 14
Array type 1
countries as String[0] // Basic declaration, this creates an empty string array, I guess 'as String []' could also be used, but you'll see why I didn't later on
Print( ArrayCount( countries ) ) // Prints: 0
ArrayInsert( countries, "England" ) // Inserts to the back of the array
ArrayInsert( countries, "Peru" ) // Inserts to the back of the array
Print( ArrayCount( countries ) ) // Prints: 2
Print( countries ) // Prints: England, Peru
ArrayInsert( countries, "Germany", 1 ) // The index specifies the index the added element will occupy once inserted, so in this case index 1
Print( countries ) // Prints: Germany, England, Peru
ArrayRemove( countries, 1 ) // Removes element 1, "Germany"
Print( ArrayCount( countries ) ) // Prints: 2
Print( countries ) // Prints: England, Peru
// Alternatively, using method syntax (which can be added at a later date)
countries.Insert( "Italy" )
Print( countries.Count() ) // Prints: 3
Print( countries ) // Prints: England, Germany, Italy
Array type 2
vehicles as String[5] // Creates an array with 5(1-5) indices of empty strings
vehicles[1] = "Boat"
vehicles[2] = "Car"
vehicles[3] = "Gary Coleman"
vehicles[4] = "Bike"
vehicles[5] = "Scooter"
Print( vehicles[3] ) // Can't mix this up with a function call
vehicles = String[10] // Overwrites vehicles with an array of 10 empty strings, the type of the new array must match
Array type 3
names = String[3] // names is automatically declared as type String[]
names[0] = "Bill"
names[1] = "Ted"
names[2] = "Steve"
Print( names ) // Prints: Bill, Ted, Steve
ArrayRemove( names, -1 ) // Negative means from the end, so -1 is the last index, -2 is the 2nd to last
Print( name ) // Prints: Bill, Ted
ArrayResize( name, 10 ) // Expand it to 10 elements, retains Bill and Ted
Print( ArrayCount( name ) ) // Prints: 10
Print( name ) // Prints: Bill, Ted, , , , , , , ,
ArrayRemove( names, 1, -1 ) // Three arguments specifies a range to remove, 1 is the first index, and -1 is the last, so this is short for erasing the whole array
Print( name ) // Prints nothing
Array type 4
board as String[3][3] // Tick-tack-toe board size
board[2][2] = "X" // Centre
// Note: ArrayInsert/ArrayRemove/ArrayResize etc don't really make sense with multi-dimensional arrays, and wouldn't work with them
bigger3DBoard as String[6][6][6]
biggerBoard = board // Copy the smaller board into this 3D one, it will be copied to the range [1-3][1-3][0], if it's smaller then the overflowing elements will be dropped
Resource type: Image
blood = LoadImage( "blood.png" )
position1 = Integer2( 5, 10 )
position2 = Float2( 7.4, 62.4 )
PasteImage( blood, 5, 10 )
PasteImage( blood, position1 )
PasteImage( blood, position2 ) // Either these functions are overloaded to take all these types, or the vector types can implicitly cast
blood.Paste( 8, 100 ) // maybe even have methods like this as well?
Print( ImageSize( blood ) )
Print( blood.Size() ) // Prints: 256, 256 or whatever, because size() returns an Integer2, notice I use a function call here rather than a variable like with the vectors, this is because you shouldn't be able to modify size from here and it shows the clear difference
Function doSomething( original as Image ) // This would allow functions to force you to pass valid images, in DBPro integers have no meaning and can easily result in broken code
crazyVersion as Image( original ) // Clone it
// do some crazy operations to change it
EndFunction crazyVersion // Return it, because we don't use resource indexes, such code would be very portable
So as you can see, should you stick to your die-hard DBPro ways, your code shouldn't require much modification, and at least with the arrays, I feel it both makes more sense than the DBPro implementation, but it's also clearner if you looking at it from a DBPro-style syntax point of view