Language Comparison: Provided Types
I recently completed working through the book Crafting Interpreters by Bob Nystrom (if you are not familiar with the book, and are even remotely interested in programming languages or compilers, I can’t recommend it highly enough). By completing the book I have implemented two distinct interpreters for a toy programming language called “Lox”, and naturally now I am convinced I am a legit language hacker and desperately need to prove myself by implementing my own programming language.
Motivation: Why Do Type Systems Matter?
A programming language’s type system (or lack thereof) fundamentally influences the way in which we express programs in that language.
Algorithms + Data Structures = Programs by Niklaus Wirth (not conincidentally, this is also the name of one of my favorite podcasts).
Crystal
Haskell
Python
Python famously (or infamously, depending on your perspective) does not enforce any static type constraints. Instead, the language relies on dynamic typing.
The “protocol view” of Python. I was first introduced to this concept in the talk So You Want to Be a Python Expert by James Powell from PyData Seattle 2017.
All values in Python support truth-testing. The language also provides the literals True and False which are always truthy and always falsey, respectively.
Strings are represented in Python by the str type.
Python supports three numeric types: int, float, and complex.
Python provides several built-in sequence types: list, tuple, and range.
Python provides built-in support for several binary types including bytes, bytearray, and memoryview.
Swift
While Swift might not technically fall into the “scripting language” category, I find that programming in Swift often feels like programming in a scripting language. This is largely a product of the language’s high-level syntax, powerful type-inference, and generally the productivity that it enables. How ever one categorizes the language, I find the Swift type system a pleasant one with which to work, rendering it worthy of inclusion in this post.
Swift represents boolean values with the Bool type. Instances of Bool may be initialized with the literals true or false or as the result of a boolean expression. Unlike other languages (e.g. Python), Swift does not support implicit conversions to Bool in conditional contexts. Modern C/C++ compilers already warn on such implicit conversions, but this is still a nice feature to call out.
// Initialize a boolean value
var truthy = true
// Toggle the value (really the only interesting operation on booleans)
truthy.toggle()
Swift provides three numeric types: Int, Float, and Double. The Int type represents signed integral values; on 32-bit platforms it is backed by a 32-bit representation while on 64-bit platforms it is backed by a 64-bit representation. Unsurprisingly, the Float type represents a 32-bit floating point value and the Double type represents a 64-bit floating point value.
// Infer an Int type
var ten = 10
print("\(ten.isMultiple(of: 5))") // true
// Explicitly specify a Double type
var twentyFive : Double = 25
// Invoke a method
var five = twentyFive.squareRoot()
Swift strings are represented by the String type which utilizes Unicode encoding. Like other languages, a String instance is actually a sequence of Characters which compose the string. Swift offers a comprehensive set of methods on the String type that allow one to interact with String instances at both high and low (underlying encoding) levels.
let hello = "Hello"
let world = "World"
let helloWorld = hello + " " + world
print(helloWorld.count) // 11
Finally, Swift provides three collection types in the standard library: Array, Set, and Dictionary. The Array type is a simple, dynamic sequence container analogous to Python’s List or C++’s std::vector. However, unlike Python’s List, each item in an Array must have the same type (expected for a statically-typed language like Swift).
// Create a new, empty array
var algoPeople = ["Cormen", "Leiserson", "Rivest"]
// Append to the end of the array
algoPeople += "Stein"
// Iterate over the array
for person in algoPeople {
print("\(person) is a co-author of Introduction to Algorithms")
}
As one might expect, the Set type is used for unordered collections of unique items, each of which must be of the same type.
// Create a new set with a set literal
var instructions: Set = ["push", "pop", "ret"]
// Insert a new item into the set
instructions.insert("cmp")
// Query the set for membership
if instructions.contains("mov") {
print("Turing Complete!")
} else {
print("Not Sufficiently Expressive!")
}
Swift’s dictionaries are used to implement and unordered mapping from a common key type to a common value type.
// Create an empty dictionary mapping String -> Int
var nameToAge : [String : Int] = [:]
// Create a dictionary from a dictionary literal
var nameToAge = ["Alan Turing": 13, "Ada Lovelace": 37]
// Query a key
let turingsAge = nameToAge["Alan Turing"]
// Update a value
nameToAge["Alan Turing"] = 42
One interesting aspect of the Swift type system is the fact that none of the types listed above are built-in to the language implementation; instead, all of these types are included in the Swift standard library. The original designer of the language, Chris Lattner, stated in an interview that this design is intended to allow users of the language to implement their own types that look and feel like primitives. This seems to go hand-in-hand with Swift’s dedication to value semantics.