Datatypes
Description of the data types handled by Drizzle
Below is a list of all the different built in datatypes that are in my thoughts for Drizzle.
Some of them I am currently unsure about, but for the most part these types should be pretty set in stone.
any
is a special type that is used for type hinting collection type variables where the types of all the possible items in the collection is unknown.list[any]
allows a list of any type objects (list
also works).tuple[any]
allows a tuple of any type objects (tuple
also works).dict[str, any]
is a dictionary with string keys and any type values.dict[any, any]
allows both keys and values to be any hashable type (dict
also works).set[any]
is a set of any hashable type- Please note that
any
only works for collection types. For now, I don't think like you should be allowed to declare anany
type variable.
null
is a special variable that represents nothing.- In cases where a variable can only be
null
, the variable can be typed with thenone
type - If a variable can be
null
then the type must be suffixed with a?
character, indicating that the variable is nullable.let a: list[int] = null # ERROR
let b: list[int]? = null # OK
- Return types can also be nullable.
def f() -> str = return null # ERROR
def f() -> str? = return null # OK
- See the section on Nullable Types for more information
- If a type declaration has a
?
suffix, then the type is considered nullable and can havenull
assigned to it. - If a user tries to use a nullable type without checking for
null
values the interpreter will warn them. - Nullables can be converted into their normal types by appending the
!
character to the variable name.let s: str? = 'abc' # s is of type str?
s = s! # now s is of type str
- If the
!
is used when the value of a nullable is null, an error will be thrown - You can check the value of a nullable by instead appending the
?
character.- This will return a boolean stating whether the value of the variable is null or not
let s: str? = null
# Here, s is of type `str?`
if s? {
# Here we know s is not null, so s becomes the `str` type
}
else {
# Here we know s is null, so s becomes the `none` type
}
let t: str? = 'abc'
# Type of t is `str?`
t = t!
# Type of t is now `str`
int
.- 64 bit integer.
float
.- 64 bit floating point number.
oct
- Octal number
hex
- Hexadecimal number
num
is a super type for all numeric types that can be used for param and return types in functions that can handleint
andfloat
types.- Like Ruby / Crystal, the numeric types in
Drizzle
will have methods (x: float = 3.as(float)
). - Currently, I think we can just have a single
int
andfloat
type for 64-bit integers and 64-bit floats for now.
'Hello, World'
.- String templating follows Ruby / Crystal style (
'We just converted #{x} into a float'
). - For typing,
str
is used.
true
,false
.- For typing,
bool
is used.
a: list[int]: [1, 2, 3]
b: list: [1, 'abc', 3.1415]
- Lists are mutable, like in Python
b[0] = 2 # => OK
c: tuple[int] = (1, 2, 3)
d: tuple = (1, 'abc', 3.1415)
- Tuples are immutable
d[0] = 2 # => ERROR
- A nice thing that having specified typing gives us is the possibility to do
let x: tuple[int] = (1)
and not have to add the extra,
to denote a one-item tuple
map: dict[str, str] = {'a': 'b', 'b': 'a'}
map2: dict[str, any] = {'a': 'b', 'b': 2}
map3: dict = {0: 'a', 'b': 1}
s1: set[int] = {1, 2, 3}
s2: set = {1, 'b', 0xa}
A big plus Drizzle has over Python in this regard is that, due to Drizzle's type declarations, the
{}
literal can be used to define empty sets and empty dicts with no hassle*!* A small bit of hassle might arise when using the literal in a conditional (
if s == {}
) but hopefully the parser will be able to handle it.Following a similar style to Crystal, ranges are defined in two ways;
0..5
- Contains the numbers from 0 to 5 inclusive (0, 1, 2, 3, 4, 5)
0...5
- Contains the numbers from 0 to 5 exclusive (0, 1, 2, 3, 4)
As a metaphor to remember this, you can imagine that adding the third dot pushes the 5 out of the range.
When something can be one of a select few types, e.g values in a
dict
, then they can by typed using union types.Union types are simply a list of types separated by the
|
character.For example,
dict[str, str | int]
is the type for a dictionary with string keys and either string or integer values.As Drizzle supports higher order functions, meaning that functions can be passed as parameters, then these functions need to have a type hinting syntax.
The syntax is as simple as possible, simply the types in the argument tuple, the return type operator and the return type, all contained in parentheses.
For example,
((num, num) -> num)
is the type for functions that take in two num
type parameters and return a num
type parameter.To conserve time and energy, type aliases can be set up for cases where you have a large type definition that is used in multiple places.
Let's take the following value as an example;
[
[1, 'abc', {'a', 1}],
{'a': 1, 'b': ['a', 1, {'a': 'b'}]},
]
The type declaration for this monstrosity lovely value should look something like
list[list[int | str | set[str | int]] | dict[str, int | list[str | int | dict[str, str]]]]
.Imagine if you were using this type of data as a parameter and return type for many different functions. Sure you could copy and paste, but what if you had to allow integer keys in the dict of the list of the dict.. You get the picture.
Drizzle allows you to create a type alias without much effort, that you can use anywhere in the code. Let's take a look at the two ways we can create a variable to house this value;
# Not so good
let v: list[list[int | str | set[str | int]] | dict[str, int | list[str | int | dict[str, str]]]] = get_data()
def get_data() -> list[list[int | str | set[str | int]] | dict[str, int | list[str | int | dict[str, str]]]] {
return [
[1, 'abc', {'a', 1}],
{'a': 1, 'b': ['a', 1, {'a': 'b'}]},
]
}
# Much better
const monstrosity: type = list[list[int | str | set[str | int]] | dict[str, int | list[str | int | dict[str, str]]]]
let v: monstrosity = get_data()
def get_data() -> monstrosity {
return [
[1, 'abc', {'a', 1}],
{'a': 1, 'b': ['a', 1, {'a': 'b'}]},
]
}
There's nothing stopping you from writing your code in the first style, but it's probably far more preferential to use type aliasing in a situation like this. Hopefully, however, a type like this doesn't show up in your code all that often...
Last modified 3yr ago