# 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`