Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

Types

MiniD is dynamically typed, meaning that there are types, but variables are not typed, only the values they hold. So it's perfectly legal to write

local x = 5;
x = "hello";
x = [4, 5, 6];

As far as the strength of the typing is concerned, MiniD leans toward the strong side. This means that the language tends not to perform implicit conversions between types. One exception is that if a mathematical operation is performed on one integer and one floating-point number, in which case the integer will be implicitly converted to a float.

There are two categories of types: value types and reference types.

Value Types

Value types have value semantics, that is, they are copied when they are assigned into a destination. Value types are not allocated on the heap unlike in some languages (like Python), so you don't have to worry about, for example, a loop full of number-crunching generating a ton of garbage.

TypeDescription
nullAbsence of a useful value
boolTruth value
intSigned 2's complement 64-bit integer
floatDouble-precision IEEE 754 floating-point number
charSingle Unicode codepoint

null is the value with which all uninitialized variables are initialized. null is a type of its own, and has only one value, which is null. null always equals (and is identical to (using "is")) null. In multiple assignments, extra variables on the left-hand side are given the value null. When calling functions, if not all parameters are given values, they are given null instead.

A bool can only hold the values true and false. You cannot perform any kind of mathematical operations on them. You can only assign their value, test for equality, and compare them (false is less than true).

An int is defined as as 64-bit signed integer. 64-bit architectures are becoming ubiquitous, and even most modern 32-bit architectures can perform 64-bit calculations almost as quickly as 32-bit calculations.

A float is the same as a double found in many C-style languages: a double-precision IEEE 754 floating-point number.

In Lua, there is only one number type, and it (usually) corresponds to a double-precision floating point number. While this simplifies the implementation and also allows for any value that a 32-bit integer can store to be stored, the problem with this is speed. Most calculations, in general, are performed using integers. Things like loop and array indices really only make sense with integers. It seems rather inefficient to use a 64-bit floating-point type to loop from 1 to 10.

A char is like D's dchar. It can hold any Unicode codepoint. You can compare characters, but they will be compared by value and not lexicographically. Characters can be concatenated together to form strings, and can be concatenated with strings as well.

Reference Types

Reference types are allocated on the heap, and when assigned into a destination, only a reference to the same object is copied and not the object itself.

TypeDescription
stringA sequence of Unicode codepoints
tableA set of key-value pairs, where keys can be any type (except null and boolean)
arrayA list of values of any type, indexable by consecutive integers starting at 0
functionA reference to a function closure
classA user-defined type
instanceAn instance of a class
namespaceA special sort of table for holding declared variables
threadA collaborative thread of execution
nativeobjA lightweight reference to a host-application object
weakrefA weak reference to any reference-typed object

Strings are fairly obvious. In MiniD, they are essentially encoded in UTF-32 giving a sequence of Unicode codepoints. This is for two reasons: one, compared to UTF-8 and UTF-16, there are no multibyte encodings to deal with. And two, compared to other encodings, using Unicode means that MiniD strings can hold virtually any character in any language ever to have existed (or not existed, as well as all kinds of other confusingly-included symbols). Can't say the same about ASCII, or Latin-1, or whatever the current locale's 8-bit character set happens to be.

Note that implementations are free to represent strings internally using alternative encodings (i.e. switching to UTF-8 if nothing above U+0007F were used, or using more exotic nonstandard encodings like record1). The only requirement is that the language sees strings as if they were a sequence of Unicode codepoints.

In MiniD, unlike in D, strings are immutable. When concatenating strings, a new string is created; same with appending strings. Because this can make string manipulation rather awkward or inefficient, MiniD provides the StringBuffer class in the base library to serve as a mutable string type.

Strings are reference types but their immutability puts them in an odd place somewhere between a value and a reference type. Implementations are required to "intern" strings; that is, for a given string value, there will be exactly one string object that holds it. This way, common strings (such as field and global names) will not be unnecessarily duplicated, and a test for equality can internally be replaced with a simple test for identity. The result of this is that for any two string objects a and b, "a == b" is equivalent to "a is b" (hence why they seem to be like value types).

Tables are a useful container type. They are a kind of map, mapping from any type except null to any type except null. Not being able to have null values seems like a disadvantage, but in fact, if a table is indexed and the key does not exist, null is returned, which means it's more like null values are stored for free.

Tables do not have quite the power that they do in Lua, however; most of the object-oriented aspects that can be achieved in Lua are accomplished using classes and instances in MiniD, and many other uses are covered by namespaces.

Arrays are what you'd expect - a sequential list of values of any type, indexed by integers. Indices start at 0, and go to the length of the array minus one (as in most C-style languages).

Arrays exist, like integral types, for both performance and conceptual reasons. In Lua 5, tables can act like arrays, based on an algorithm that detects if the table is being used as an array, in which case the values will be stored in a true array instead of in the hash map. This is complex, and a fancy algorithm will never be as fast as the real thing. When you need a list of values indexed by (contiguous) integers, use an array; otherwise, use a table.

Slices of arrays always point into the array from which they were sliced, making it possible to manipulate subsections of arrays (i.e. sort just a small part of an array). Concatenating arrays always creates a new array object. Appending to an array (either with ~= or with the append method) will attempt to expand the array in place, but if there is not enough room, will reallocate the array.

Functions are references to function closures, either written in MiniD or created by the host program. A function closure has everything it needs to properly execute the function. See Functions for more information on closures.

Classes are user-defined types. Instances are just instances of those classes. MiniD uses a simple class-based object system, similar to those found in D or Java but much simpler and more dynamic. See Classes for more information.

Namespaces are a lot like tables. However, their keys are restricted to strings, and they can hold null values. The ability to hold null values is important, because unlike tables, attempting to access something from a namespace that doesn't exist will throw an error. These are used mostly as symbol tables, for things like global variables, modules, and class/instance fields.

Threads are created when you use a coroutine expression. They represent a separate thread of execution with its own call stack. Threads in MiniD work collaboratively; that is, a thread will only stop executing when it uses a yield expression or when it returns from its main function. See the functions section for more info.

Native Objects are a very lightweight references to objects held by the host application. What that actually means may depend upon the implementation. For example, in the reference D implementation, native objects are used in order to hold references to D objects that are allocated on the host app's heap, and are handled specially so that the host application will not collect the referenced D objects. In a C implementation, they might just be simple pointers, and in fact might be implemented as value types instead. In any case, native objects cannot be created by script code and have no operations defined for them other than identity.

Weak References are a way to get a reference to another reference types without controlling its lifetime. Normally, if an object is transitively accessible through any chain of references from either the stack or the globals, that object is considered alive and is not collected by the GC, and if it's not accessible, it will be collected. Weak references allow you to sort of keep tabs on an object without the GC thinking that you want it to be alive. You can create a weak reference to an object, and if no "strong" references to that object exist, it will be collected and the weak reference will be updated to reflect that. Weak references cannot be created to value types as that has no meaning.

Weak references must by their nature be interned: for any reference type object, there can be only one weak reference object that references it. Therefore, if two weak references are identical, they refer to the same object.

Weak references have a somewhat special relationship with tables. If a key and/or value in a table is a weak reference, and the object that the weak reference refers to is collected, that key-value pair is removed from the table. Implementations are required to ensure that 'null' weak references are removed from tables either immediately or in some manner that imitates it.