about
Bits Introduction
08/02/2024
Bits Repo Code Bits Repo Docs

Bits Introduction

C++, Rust, C#, Python, JavaScript, and oh my!

These pages are intended to help you learn a new language by comparing similar code fragments in a language you know and one you want to learn.

1.0 Introduction

Professional software developers often need to be fluent in more than one language. In these Bits we discuss five languages that are widely used in the developer communities. One notable omission is the Java language, left out because Java and C# are very alike, feature by feature. We chose C# because of its deep integration with the Windows run-time, but also may be used with the Linux and macOS platforms. The details dropdown, below, puts the description of our five focus languages into a larger context of a long history of popular languages.
History of Programming Languages 

Table 1. Language Descriptors

Bst Block-Structured:
Code composed of blocks delineated with braces or indentations. Each block creates a stackframe for allocation of local resources when thread of execution enters and disposes on exit.
Examples: Algol, C, C++, Rust, C#, Java, Python, JavaScript, ...
Imp Imperative:
Source code describes steps to execute.
Examples: Algol, C, C++, Rust, C#, Java, Python, JavaScript, ...
We will focus on imperative languages in these Bits.
Dcl Declarative:
Source code specifies results of computation.
Examples: Swift, Elixir, Clojure, HTML, SQL, Prolog, Lisp
Lgc Logic Programming:
A language that describes program intent with a set of rule clauses that a solution must satisfy through logical reductions, e.g., also Declarative.
Examples: Prologue, Mercury, CLP(FD), ... Lisp and Clojure have some logic programming features
Fun Functional:
A language that describes program intent with a set of function compositions and specialized data structures. Functions are expected to be first-class, e.g., can be passed to and returned by other functions. Often data is immutable and operations are implemented with recursions.
Examples: Lisp, Haskel, ML, OCaml, Clojure, ... Rust, Swift, C#, JavaScript, and Python have some functional features like lamdas (anonymous functions that may capture local data).
OOP Object Oriented Programming:
Languages that define objects and support the relationships: inheritance of implementation, composition, and aggregation.
Examples: Swift, C#, JavaScript, Java, Ada, C++, ... Rust and Go support inheriting traits or interfaces but do not support inheritance of implementation, e.g., concrete base classes.
Dyn Dynamic typing:
Types of variables are determined at run-time by binding to data.
Examples: Elixir, R, JavaScript, Python, Perl, Erlang, MATLAB
Mng Managed:
Compiles to bitecode that is translated for hosting in a virtual machine. Usually uses garbage collection and may support reflection.
Examples: TypeScript, Elixir, Clojure, C#, JavaScript, Java, Python, Erlang

Table 2. Language History

"Created" is an approximation of the first public stable release of each language.
Language Created Bst Imp Dcl Fun OOP Dyn Mng Comment
Rust 2015 Rust provides traits but no inheritance of implementation, compiles to native code, and has memory and data race safety by construction.
Swift 2014 Swift is a multi-paradigm language, supporting both imperative and declarative programming styles..
TypeScript 2012 TypeScript is a statically typed language that is based on JavaScript.
Elixir 2011 Elixir is based on the Erlang language with a focus on high performance concurrency.
Go 2009 Go provides interfaces but no inheritance of implementation, and uses garbage collection but does not rely on a virtual machine.
Clojure 2007 Clojure is a functional language that relies on a stack-based virtual machine.
R 2000 R supports both imperative and functional styles with dynamic typing. It is used to process and display data.
C# 2000 Uses compilation to bitecode then translation to a stack-based virtual machine. Has value, reference, and dynamic types. Also provides Language Integrated Query (LINQ).
Language Created Bst Imp Dcl Fun OOP Dyn Mng Comment
CSS 1996 Cascading Style Sheets (CSS) is a declarative language used to set styles for web programs.
JavaScript 1995 Scripting language uses unique execution model with event message queue and stack of function stackframes. Widely used for web programs.
Java 1995 The first to use compilation to bitecode then translation to a stack-based virtual machine
Python 1991 High-level dynamically-typed scripting language
HTML 1990 A markup language used to structure content in web pages
Haskell 1990 A lazy-evaluation functional language, often used for type system research
Perl 1987 Interpreted language with strong string handling features using regular expressions
Erlang 1986 One of the first modern functional languages
Ada 1983 language developed under DOD contracts with safety focus
C++ 1979 One of the first object-oriented programming languages
Language Created Bst Imp Dcl Fun OOP Dyn Mng Comment
MATLAB 1978 Focus on simulation, numerical computing, and data analysis
SQL 1970s One of the first data programming languages
Prolog 1972 One of the first logic programming languages
C 1972 Designed to write code for Unix
Pascal 1970 Designed to be a teaching language
Lisp 1958 The first AI language
Algol 1958 The first modern block-structured language
Fortran 1957 One of the earliest high-level languages
Below are some definitions that concentrate ideas so we don't have to repeat them later threaded throughout the language discussions.
Language Definitions  

Table 3. - Definitions

Type and Data definitions
Data A configuration of bytes that may be contiguous in stack memory, e.g., int, double, ... or split between a control block in stack memory and a collection of values stored in heap, e.g., string, vector, map, ...
Variable A name that is bound to an instance of data.
Type A specific structure of data with an allowed set of operations determined by the Type implementation, usually with a class or struct.
Static type Type defined at compile time, fixed for the lifetime of program. Type applies to both variable and data.
Dynamic type Variable type defined at run-time by binding to functions and data of a given type. Variables can be reset to data of a different type at any time during program execution and may be bound to functions defined at run-time.
Strict typing Data may be coerced to a different compatible type only by explicit conversions which usually create new data.
Weak typing Data of given type may be coerced to another compatible type, using implicit or explicit casts. Coercions may create new data or may simply reinterpret existing data.
Generics Function f(t:T) or class C<T> of unspecified type T at design time. T is specified at the time of compilation of the executable it serves.
f(t:T) is a pattern for producing functions, e.g., f(i:int), f(d:double), ..., and C<T> is a pattern for producing classes, e.g., C<int>, C<string>, ... , which are in turn patterns for creating instances, e.g., let c = C<int>::new();
Type specific definitions
Copy (Value) Type Copy construction, assignment, and pass-by-value operations copy values of the source to the destination. Source and destination are independent instances.  [ show copy diagram ]
Move Type Copy construction, assignment, and pass-by-value operations move resource values of the source to the destination. This is usually just a copy of a pointer to the source's resources. The source is no longer valid. Note that this is a change of ownership.  [ show move diagram ]  
Source invalidation can be avoided by making a clone for the operation. The clone is invalidated but the source from which it is cloned is still valid. Rust and Python provide facilities to easily build clones.  [ show clone diagram ]
Reference Type Copy construction, assignment, and pass-by-value operations copy references pointing to the source instance (always on the heap) to the destination. This results in two references to the same heap-based instance.  [ show ref diagram ]
Languages like C# and Java use syntax that looks like operations are applied to instances, e.g., a = b. However, if a and b are referece types, the operations are actually applied to handles that point to a and b.
Type Relationships
Inheritance A design process that uses a base type as part of a more specialized derived type, automatically exposing public members of the base as public members of the derived.
Some languages allow use of base implementation as part of the derived implementation, some languages allow only declarations of base methods to be declarations of the derived.
Composition A design process that uses an instance of a composed child type as a member of the composing type.
Composition results in the child instance embedded in the memory footprint of the composer. Construction, assignment, and pass-by-value result in two independent instances, e.g., the source and destination1.
C++ and Rust types can compose instances of arbitrary types. C# can compose only Value types, e.g., primitive types and arrays and structs of primitive types. Python and JavaScript cannot use composition. All their instances reside in a managed heap where instance and child members have distinct memory locations.
Aggregation A design process that uses a pointer or managed handle to an aggregated type as a member of the aggregator.
Aggregation results in a child instance placed in a native or managed heap2 at a location distinct from its aggregator type and referred to with a handle. Construction, assignment, and pass-by-value result in two references to the one source instance.
C++ and Rust can use composition and aggregation with any type. C# can use composition only with Value types and aggregate only reference types. Python and JavaScript can only use aggregation3
  1. Move types complicate construction, assignment, and pass-by-value. These operations invalidate the moved source. Primitive types in C++, Rust, and C# are copy types. All library and user-defined types in C++ have copy constructors and move constructors. So a C++ type may be both copy and move. Which operation is used, Copy vs. Move, is determined by program context.
    Any Rust types that contain data in the heap are move types. C# value types are copy, all other C# types are reference. Python and JavaScript types are all reference. They do not provide move operations.
    Rust's type system, including Move types, simplify the language by defining semantics of construction and assignment as part of the language type system, rather than requiring overloaded constructors and assignment operators to define those semantics.
  2. Technically C++ and Rust can aggregate an instance residing in stack memory. That is almost never done for lifetime safety reasons.
  3. Many developers refer to aggregations as "compositions" of their type. That is technically incorrect. They are compositions of handles to instances of their type.
These Bits are intended to help both new and more seasoned developers who need a solid introduction to one or more of the languages presented here.

2.0 Languages

These five languages are all imperative, but have a few operations similar to functional languages like Haskel and OCamel. That is especially true of Rust with its iterator adapters. We will focus on the imperative aspects of this gang of five.

Table 4. - Defining Language Features

Code Repo C++ Rust C# Python JavaScript
Highs:
All five languages are supported on Windows, Linux, and macOS
C++ has:
  • deep support for creating user-defined types
  • excellent performance
  • strong static typing so many errors are caught at compile time
  • large collection of std libraries
  • widely used with many third party libraries
  • a lot of learning materials
Rust has:
  • Compiler enforced memory and data-race safety
  • excellent performance
  • very strong static typing so most errors are caught at compile time
  • simple value behavior with copy, move, and clone operations
  • clever type system design with few context dependencies
  • Very effective tool chain
  • fairly small syntax and library footprint with excellent documentation
  • a lot of learning materials
C# has:
  • memory safety due to managed environment
  • strong static typing so many errors are caught at compile time
  • direct support for event handling using delegates
  • effective data handling with language-integrated-query (LINQ)
  • deep integration with Windows platform
  • a lot of learning materials
Python has:
  • memory safety due to managed environment
  • duck-typing so programs get to compilable status quickly
  • simple sytax organized with white-space indentations
  • very large collection of third party modules
  • a lot of learning materials
JavaScript has:
  • memory safety due to managed environment
  • duck-typing so programs get to interpretable status quickly
  • simple sytax organized with braced scopes
  • simple object definition
  • tightly integrated with HTML and CSS
  • standard language is available on all major browsers
Lows:
C++:
  • very complex language, due in large part to its many context dependencies
  • a lot of effort to become proficient due to large syntax and libraries
  • many paths to undefined behavior, e.g., reading and writing to unowned memory
Rust:
  • safety restricts the way references can be used - takes some getting used to
  • long compile times for large programs due to compiler safety checks
C#:
  • weak latency and throughput performance due to managed environment
  • three very different type categories, e.g., value, reference, and dynamic
Python:
  • poor latency and throughput performance due to managed environment
  • errors are discovered at run-time with uncaught exceptions
JavaScript:
  • weak latency and throughput performance due to managed environment
  • scripts fail silently with uncaught exceptions
Typical applications
  • system programming
  • utility programs
  • scientific computing
  • system programming
  • utility programs
  • scientific computing
  • cloud apps
  • web apps
  • prototyping
  • scientific computing
  • web apps
Host Newly created process
Program Execution
Newly created process
Program Execution
Virtual Machine
Stack-based execution engine: Managed Execution Process
Virtual Machine
Interpreted execution of code blocks: Execution Model
Virtual Machine
Single-threaded event loop with execution stack and event queue: Event Loop
Managed No
Compiles to native code, supports weak reflection.
Very permissive about code operations. Assumes developer understands full consequences of every line of code.
No
Compiles to native code, supports weak reflection.
Restrictions on allowed use of references to enable memory and data race safety by construction. Rust allows pointers only in unsafe blocks1.
All .Net language codes are compiled to CIL (Common Intermediate Language, similar to Java Bytecode) and jitted to native code at run-time.
Supports garbage collection and strong reflection. GC eliminates most memory safety issues at the expense of lower performance due to GC operation.
Parsed to blocks and interpreted
Supports garbage collection and strong reflection. GC eliminates most memory safety issues at the expense of lower performance due to GC operation and interpretation.
Code jitted and interpreted
Supports garbage collection and strong reflection with caveats. GC eliminates most memory safety issues at the expense of lower performance due to GC operation.
Types Strong static typing
Copy and Move
Strict static typing
Copy and Move:
Bind, Copy, Move, Clone
Strict static typing and dynamic typing
Value, reference, and dynamic types
Dynamic typing
All references to heap-based instances. Types apply to data, not variables.
Dynamic typing
All references to heap-based instances. Types apply to data, not variables.
Generics Templates resolved at compile-time, strong support for template metaprogramming, specialization of classes, and overloading of functions. Generics resolved at compile-time. Generic type can be bound by trait, but no specialization or overloading. Generics resolved at run-time. Generic type can be bound by constraints
C# Constraints
No need for generics due to dynamic typing No need for generics due to dynamic typing
Inheritance Multiple inheritance of implementation Multiple inheritance of trait declarations (similar to C# interfaces). No inheritance of implementation. Single inheritance of base class implementation, multiple inheritance of interface declarations. Multiple inheritance of base class implementations Single inheritance of implementation using prototype chain
footnotes:
  1. Most Rust developers try to avoid use of unsafe blocks. None of the Rust code in this site - many thousands of lines of code - directly uses unsafe blocks. Unsafe blocks are used by some library type implementations, but are very carefully vetted. An unsafe block suspends some of the Rust language safety rules, but should be constructed to provide a safe interface.
C++ Rust C# Python JavaScript

3.0 - What's Next?

Each of the features described in Table 2. will be explored with code examples in succeeding Bits. If you click on the "Pages" button at the right on the bottom menu you will see a list of the completed and planned Bits. The Bits present code samples from the five languages with step-by-step instructions for downloading and configuring tools needed to build and run them. The next Bit, Bits_Tooling, presents one of two main ways to examine code samples, e.g., cloning the code repository and opening examples in the Visual Studio Code (VS Code) editor. Using plugins for each of the languages the examples can be built, run, and walked with a debugger.
  Next Prev Pages Sections About Keys Goto Mark