349 lines
13 KiB
Markdown
349 lines
13 KiB
Markdown
|
|
# Principles
|
|
|
|
## Function Overloading
|
|
Functions should be overloadable as long as they don't have the exact same signature.
|
|
This allows for keeping a clean naming of functions and minimizing name collisions.
|
|
|
|
## Function Parameters
|
|
Functions shouldn't take more than 10 parameters.
|
|
If more Information is needed, functions should instead require reasonable structs.
|
|
Functions shouldn't have default paremetrs, but Configuration structs can.
|
|
This Keeps Data in structs and upholds the purpose of both.
|
|
Optional Paremeters should be done through some Optional Data Type, and should not
|
|
have their own syntax, because it infact increases confusion inside a language and
|
|
people might end up with needing to write that syntax a lot of times depending on
|
|
how its implemented.
|
|
|
|
## References and Values
|
|
It should be obvious by syntax what is a refernce and what is a value,
|
|
this is especially important for library functions and interfaces
|
|
|
|
### Pointer Arithmetic
|
|
Pointer Arithmetic is 99.9% of the time not needed and tends to do more harm.
|
|
Pointers/References should not be messed with to achive a stable language
|
|
|
|
## GC Optimization
|
|
If a language is compilet and uses a GC, the GC should be optimized for speed.
|
|
Go and Rust clearly show that its possible through different approaches.
|
|
|
|
## Line Endings
|
|
A Programming language should be precise and thus only use one kind of line endings.
|
|
The One compatible with the most systems is the Unix Linefeed.
|
|
|
|
## Reflect
|
|
For a lot of Programming Paterns and Frameworks it is a crucial part
|
|
to have reflection, which allows flexebillity and to inverse
|
|
and decouple the reliance of code on each other
|
|
|
|
## Decorators
|
|
Decorators are Markers which allow frameworks
|
|
to give the user an easy way to interact with it in common ways.
|
|
Decorators can also be used by a language itselve to allow for:
|
|
- compile time logic
|
|
- marking code as tests
|
|
|
|
They can also be used by other tools like linters and formatters to tell them whats up.
|
|
|
|
Another common usage of Decorators or their derivates (struct field tags) is to
|
|
add information to struct fields for (de)serilization of them.
|
|
|
|
## Preludes and Wildcard Importants
|
|
Wildcard imports allow to minimize the required amount of import statements.
|
|
Preludes try to fix domain collision from wildcard imports,
|
|
by only providing the most commonly used imports,
|
|
but this does not completley fix the problem and comes with its own struggles.
|
|
In a language with only functions and no struct, wildcard imports are not needed
|
|
and code without them can actully be much more precise,
|
|
like writing `os:GetEnv` instead of `GetEnv`.
|
|
For a Language with structs, not haviong wildcard imports can be extremly tidious.
|
|
Preludes are not the Anwser to Wildcard imports.
|
|
|
|
The best middleground solution, which keeps imports and code usage readable
|
|
and precise enough is to:
|
|
1. Don't allow direct function
|
|
Resulting in:
|
|
|
|
```go
|
|
import std/io
|
|
|
|
io:println("Hello World")
|
|
```
|
|
|
|
2. Allow Wildcard Imports, but only for struct and enumns
|
|
3. Allow Package aliasing to resolve name conflicts when needed (most often in a mapper)
|
|
|
|
```go
|
|
import bar/cake as bar_cake
|
|
import foo/cake as foo_cake
|
|
|
|
fn map(bar_cake:Buzz buzz) foo_cake:Buzz {
|
|
return foo_cake:Buzz:new(buzz.name)
|
|
}
|
|
```
|
|
|
|
This allows simpler imports in most code, and only needing to be more specific,
|
|
when handeling the conversion from one package to another
|
|
|
|
## Vissibilty and Access Modificators
|
|
It is important for bigger projects and
|
|
libraries to provide visiblity and access modificators,
|
|
so data can only be accessed and edited in the inteded way.
|
|
A Bad example of this is Go, where a value is either read-writable or not evene vissible.
|
|
|
|
### Getter & Setter
|
|
A common way to controll data access are getters and setter.
|
|
Their structure is common and well know, so much in fact,
|
|
that they can be replaced by an annotation or a custom syntax like in c# `{get; set;}`.
|
|
|
|
Instead of an annotation that generated functions at compile time, having a syntax
|
|
to clearly state the readabillity and modifiabillity of values
|
|
by internal and external code is important for understanding and controlling
|
|
the program flow.
|
|
|
|
## Easy and powerfull Standard-Library
|
|
A strong Standard-Library allows people using a language to do much more
|
|
in a consistent way while not needing to import and trust 3d party libraries.
|
|
|
|
Go is a great a example of the advantages of a good Standard-Library.
|
|
C++ is an example of a powerfull, but hard do use Standard-Library,
|
|
with a lot of functions, you're not supposed to use,
|
|
because of security vulnerabillities.
|
|
|
|
### Important Parts of a Standard-Library
|
|
Following parts should be included in a powerfull Standard-Library:
|
|
- Printing and Logging
|
|
- I/O
|
|
- String Manipulation
|
|
- Array Manipulation
|
|
- (De)Serilization
|
|
- WebServer/-Socket
|
|
- environment variables
|
|
|
|
### Debugging and Printing
|
|
Everything of the Standard Library should be debugabble and printable out of the box.
|
|
Also all structs of whatever library should so, unless a field is marked to hold
|
|
sensite data, and that it should be kept in a more save memory region.
|
|
An example of such data, could be the password in an input field.
|
|
|
|
## Consitent Naming
|
|
A Good Language should have a consitent naming convention
|
|
and a consistently named Standard-Library for consistently powerfull usage.
|
|
|
|
### Formatter
|
|
To enforce such Rules,
|
|
there should also be a formatter in the builting default Tools of the Language
|
|
|
|
## Fast Compiling
|
|
A Compiled-Language should do so fast, so Developers can test fast and frequently.
|
|
|
|
## Easy Async
|
|
Async should be easy to create, work with, understand and read.
|
|
The Interaction between Sync and Async should be simple to achieve.
|
|
|
|
## Static at first Sight
|
|
It should be vissible what is and what isn't static at first sight.
|
|
Good examples for this are Rust and PHP, which both utilize `:` to indicated static,
|
|
while using another separator (`.` and `->`) for dynamic/instanced fields and functions
|
|
|
|
## Good Package System
|
|
A Good Package System should work well for small and Big Projects, while
|
|
keeping some kind of known structure across projects which everybody can easaly
|
|
undestand.
|
|
|
|
Go is a Bad example for this, becasue it isn't great to use with larger Projects.
|
|
|
|
A Good Package System should be:
|
|
- easy to host
|
|
- easy to work with
|
|
- secure
|
|
- fast
|
|
- directly mappabl from its structure on disk (physical) to its logical one
|
|
|
|
To showcase that it's logical and Physical location map to each other, `/`
|
|
is probably the best seperator, because it is used for Unix based filesystems.
|
|
|
|
## Not to much Magic
|
|
To much Magic results in Chaos, just a few sprinkles can make for a smooth experience
|
|
|
|
### Errors as Values
|
|
An Example of to much Magic,
|
|
is builtin Error Handeling into languages like Java and PHP,
|
|
which is less flexable than Errors as Values like seen in Go or Rust.
|
|
|
|
The structure of Error Handeling completley depends on the Project
|
|
and thus such a feature should be as versatile as possible.
|
|
|
|
The most flexable and still easy to work with kind of Error handeling,
|
|
that is **not** Magic, is Errors as Values.
|
|
|
|
## Strong Enums
|
|
Enums should be Strong.
|
|
This means, when an enum value is used in a match statement,
|
|
all paths should require matching **or** there should be a default case
|
|
|
|
Enums should be versitile and able to hold different kinds of data,
|
|
this allows for great patern matching capabillities.
|
|
|
|
A good example of strong Enums is Rust.
|
|
|
|
## Readabillity
|
|
A Language should be readable without syntax highlighting and any other tools.
|
|
Syntax should be specific to minimize confusion.
|
|
An example for this would be using `->` instead of `=>` because the later
|
|
could also mean "greater or equal".
|
|
Another way to achieve readabillity is to minimize language based keywords like
|
|
the `and` in Python and to use specific Charakters with single purposes like `&&`
|
|
|
|
### Clear Code Blocks
|
|
|
|
Out of experince of other languages we learn following:
|
|
- Using Indents to define codeblocks, results in hard readabillity of where
|
|
blocks end. There are tools to help with this, but it are external required tools.
|
|
- Using a start and end keyword.
|
|
An Example of this is lua, and the common problem with readabillity here is,
|
|
that people tend to dismiss them as text and not as markers.
|
|
The Learning here is to not use word as markers
|
|
- Braces are one of the most used markers and tend to not be overseen and
|
|
be single purpose, while even allowing to define sub-codeblocks easaly.
|
|
|
|
Out of existing Languages the best working marker seems to be the common braces `{}`.
|
|
|
|
## OOP, Procedual, Functional
|
|
All Patterns have their advantages,
|
|
thus a language shouldn't only focuss on one of them.
|
|
A Great failure to learn from is Java,
|
|
which foccuses to much on OOP
|
|
and thus even the main function needs a main class, with a main method.
|
|
|
|
### Lambdas and Functions as Values
|
|
For a lot of Functional Paterns, Functions need to be treated as Values
|
|
and Lambdas are commonly used to complement this.
|
|
|
|
This allows for a more Flexable Language.
|
|
|
|
### List Comprehension vs Higher Order Functions
|
|
List Comprehension and Higher Order Functions are trying to solve the same problem,
|
|
of having to write big nested loops for simple actions on lists/arrays.
|
|
List Comprehension is a quite specific sytax inside a syntax, which is not extendable
|
|
through for example bootsraping a language, while it is possible with
|
|
Higher Order Functions.
|
|
Higher Order Functions are more precise, readable and flexible than List Comprehension.
|
|
|
|
### Structs vs Classes
|
|
Structs only hold data, Classes also implement functionality.
|
|
Classes can be extremly limiting, when everything is a class.
|
|
Structs are extremley simple to understand.
|
|
Go did go a middleway, with allowing you to implement functions on structs.
|
|
Rust took it a step further by Improving their Interfaces (traits),
|
|
but they still dont have inheritance.
|
|
|
|
### Inheritance and Interfaces
|
|
Sometimes OOP is the best Patern to solve a Problem,
|
|
for it to be usable, some kind of inheritance is required.
|
|
inheritance is greatly complemented by Interfaces.
|
|
|
|
Allowing the implementation of interfaces for not owned code,
|
|
can provide a lot flexebillity, but it also means that caching is a lot harder.
|
|
|
|
## Multiple Return Values
|
|
Returning multiple values is an extremly powerfull feature,
|
|
which stops the need for custom structs which only hold the data that's returned.
|
|
|
|
A Great example is Go, which builds a lot of its features around this concept.
|
|
|
|
## Pattern Matching
|
|
Pattern matching is a common problem in Programming
|
|
and thus should be easy and nice to do.
|
|
|
|
## Generics
|
|
Generics are a Powerfull tool, that allow writing less code, while staying pecises.
|
|
They allow simplifying complex systems to its core logic, thus Generics should be
|
|
part of any language.
|
|
|
|
## Differnciating Defining and Assinging
|
|
It should be easy to differntiate between defining a variable
|
|
and assinging a value to a variable, or doing both, to
|
|
avoid confusion.
|
|
|
|
## Defering
|
|
The Concept of defering is to call a function, when the current one is left.
|
|
This allows for keeping code that is done because of something else, at the
|
|
saem place.
|
|
|
|
A Good example of this are Files. Most Operating Systems require you to
|
|
get a file handle and to close it, when you're done.
|
|
If you have multiple possible error paths, you would need to check if
|
|
the file is still open, and if yes, close it. Defering allows to simplify this.
|
|
|
|
Here is a simple Go Code Snippet that shows, how defering can keep what
|
|
matters at one place:
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"log"
|
|
)
|
|
|
|
func main() {
|
|
file, err := os.Open("some-file.txt")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
fmt.Println(scanner.Text())
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
```
|
|
|
|
Defering makes Functional code more readable and keeps code together,
|
|
that directly has to be written as a sideeffect of other code, thus it
|
|
is an important feature for a functional language.
|
|
|
|
## Typing
|
|
A language should provide strong typing.
|
|
Strong typing catches errors during compile and not runtime and
|
|
it also increases readabillity of code so that its clear for a reader
|
|
what variables are actually.
|
|
|
|
### Type Inferance
|
|
Infering a Type allows the Devloper to only specify the type once.
|
|
|
|
A Bad example would be Java, where you have to Write the type multiple Times:
|
|
|
|
```java
|
|
Object o = new Object();
|
|
```
|
|
|
|
//TODO: Decide if you like or not like Go Type inferance.
|
|
|
|
### Builtin Datatypes
|
|
It is important that the builtin data types of language are designed well,
|
|
so they are easy to understand and work with.
|
|
Another big factor is consistency across them.
|
|
|
|
### Raw String Literals
|
|
Sometimes Strings need to hold values,
|
|
which conflict with the language syntax and thus need escapement.
|
|
This escapement results in less readable strings.
|
|
The Solution to this problem are Raw string literals,
|
|
which have a custom Marker for start and end, which can be extended to fit the purpose.
|
|
|
|
A Good example is Rust with its `r#"`:
|
|
|
|
```rs
|
|
r#"This is a # Raw String Literal"#
|
|
```
|
|
|
|
Raw String Literals also allow for writing Multiline Strings without a need for `\n`
|