Programming Languages
Programming languages have the following traits:
- Legibilidade / Readability: Are its programs easy to comprehend when reading?
- Redigibilidade / Dictability: Does implementation reflect the algorithm? Is typing it succint?
- Confiabilidade / Reliability: Is it easy to find "mistakes" of the programmer?
- Eficiência / Efficiency: Does it execute quickly?
- Facilidade de Aprendizado / Learnability: Is it brief and easy to learn to program in?
- Ortogonalidade / Orthogonality: Can concepts be combined freely? (note: good orthogonality means no side effects in functions!)
- Reusabilidade / Reusability: Is it easy to reuse code in other programs?
- Modificabilidade / Modifiability: Is it easy to alter the program?
- Portabilidade / Portability: Does it run as expected in multiple platforms?
Readability → Programming languages are intended to be read by humans. As such, features like comments, and distinguishing blocks of code are imperative.
Dictability → Often, a language's dictability is inversely proportional to its readability, as having verbose code can make it easy to understand but tiresome to type out. Meanwhile, short code can be very elegant and quick to type out, but revisiting it can be harder due to low readability.
Reliability → Systems like static typing and exception treatment ("try / catch" methods for dealing with errors) can help the programmer with easily finding errors and debugging their code.
Efficiency → The time it takes to finish executing a program, desireable for intensive programs like video games, cybersecurity or research.
Orthogonality → permits easy combination of different concepts, such that functions can perform various tasks at their own time without conflicting with each other.
Reusability → benefits the user through parametrization, having a clear set of rules that lets the reusal of code be done quickly and easily.
Modifiability → relates to how much control the user has in the language's notation, such as the capacity to redefine base functions.
Portability → the capacity to run on different platforms, enforced in ways that can come at a cost to efficiency, such as Java's virtual machine, able to run on many different devices.
Backus-Naur Form
It is a notation for describing the syntax of formal languages, like programming languages. We apply it when needing the exact description of a language, such as in specifications and manuals. It is structured with rules similar to the ones we see in Formal Languages and Computer Theory, with concepts such as derivation.
When writing a BNF, there are three kinds of components:
- A set of non-terminal symbols
- A set of terminal symbols
- Rules for replacing non-terminal symbols with a sequence of symbols
<symbol> ::= _expression_
In this case, <symbol> is a non-terminal variable, and variables must be enclosed within angle brackets <>. ::= indicates a derivation, where the symbol on the left must be replaced by the expression on the right. Finally, _expression_ represents at least one sequence of symbols (terminal and non-terminal), where each sequence is separated by a | to indicate a choice.
Examples:
- <program> ::= <command> <program> | <comment> <program> | <command>
- <command> ::= <attribute> | <if> | <repeat>
- <attribute> ::= id = <expression>
- <repeat> ::= repeat <program> until <expression>
- <if> ::= if <expression> then <program> end | if <expression> then <program> else <program> end
- <expression> ::= id | number | <expression> <binary operator> <expression> | <unary operator> <expression> | (<expression>)
An example of a BNF for floating point numbers:
- <float> ::= [-] number [.number] [exponent number] | [-] number [.number] [exponent - number]
An example of a BNF for multiplying before summing:
- <expression> ::= <expression> <op1> <factor> | <factor>
- <operator1> ::= + | -
- <factor> ::= <factor> <op2> <term> | <term>
- <operator2> ::= * | /
- <term> ::= (<expression>) | number | id
Data Types and Expressions
Different data types are useful in a programming language to differentiate them, serving different purposes and being treated differently by operations. They are collections of values that contain some common property, exhibiting uniform behavior.
Types allow for easier debugging, for distinguishing the domains and ranges of a function, etc. and the type is given to a variable, usually, upon declaration. It can be inferred, by observing the kind of information stored in the variable (done by languages with type inference, like Python or JS).
Languages can also be distinguished by static / dynamic typing. While static typing is safer and more efficient, not having to reallocate memory if a variable's type changes, dynamic typing is more flexible, generally easier for begginers not worrying about data types.
Expressions are, generally, "phrases" of the program that, upon evaluation, produces a value as a result. A "pure" expression is one that does not affect anything else in the code (orthogonality!). Impure expressions have collateral effects, such as the expression: x = 3 * i++. First, the expression is executed, THEN the i++ operation is executed.
Languages can have an "arity" (aridade) property, allowing functions to receive more than one argument. C allows this as a good example of modifiability, giving you control over a function's handling of arguments.
Languages can be short-circuited or not, affecting how expressions are read. A short-circuited language will evaluate two comparisons, such as "if (a > b) || (c == 0)", only enough to get the proper response. If (a > b) is true, there's no need to check if (c == 0).
When talking about Haskell specifically, it:
- is a statically typed language → types are immutable and assigned to variables.
- is strongly typed → ?????
- has lazy evaluation → only performs expressions once their value is demanded.
- is short-circuited → only evaluates logic until answer is achieved.
NOTE FOR EXAM: Know the pros and cons of all language variations, such as typing, circuit, garbage collection, etc.
References