about
Bits Intro
12/01/2023
Bits Repo Code Bits Repo Docs

Bits Introduction

definitions, language features - C++, Rust, C#, Python, JavaScript

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.
Language Definitions  

Table 1. - Definitions

Language Definitions
Imperative Language A language that descibes program intent as a series of commands, often expressed as function or class method executions.
Examples: Algol, C, C++, Rust, C#, Java, Python, JavaScript, ...
We will focus on imperative languages in these Bits.
Functional Language A language that describes program intent with a set of function compositions and specialized data structures. Functions are expected to be first order, 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, ...
Logic Programming Language A language that describes program intent with a set of rule clauses that a solution must satisfy through logical reductions.
Examples: Prologue, ...
Type 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.
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.
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.
Type and Data definitions (tuned to Imperative languages)
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();
Object 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 user-defined types in C++ have copy constructors. 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.
  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 2. - 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#:
  • poor 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:
  • poor 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 MSIL (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 planned Bits. These 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. Another view is provided by "CodeSnaps", e.g., web pages devoted to displaying the code for each example, with a link to remote execution.
  Next Prev Pages Sections About Keys