Types
Blossom employs a static, strong and explicit typing system with immutable variables.
Type consistency is asserted during compile time, meaning that any typing mismatch or violation will result in an error, preventing the program from being built.
Empty initializations are also disallowed.
Atoms & Molecules
Atoms
Atoms are self-defining values - their identity is their value. They're immutable, lightweight and perfect for representing unique constants.
Example
@Success
@UserNotAuthorized
Molecules
Molecules are groups of related atoms, useful for creating sets of related constants.
Example
@(Red, Green, Blue)
@(Admin, Operator, Viewer)
Molecules can also be accessed using the .
operator.
Example
Color := @(Red, Green, Blue)
MyFavoriteColor: Color = Color.Blue
MySecondFavoriteColor: Color = @Green
Primitives
Primitives are the basic, fundamental data types built into Blossom. They represent the simplest possible values and serve as the building blocks for more complex data structures. Unlike composite types, primitives are atomic - meaning they cannot be broken down into smaller parts.
Bool
Boolean values are logical entities that can only have two states: True
or False
. They represent binary choices and are fundamental to computational logic.
Example
True
False
Int
Integer values are whole numbers that can be positive, negative or zero. They represent quantities without any fractional or decimal components.
Example
1517
301
-127
0
Float
Floating-point values are numbers that contain decimal points. They can represent both very large and very small numbers through standard decimal or scientific notation.
Example
3.14
-1.67
1.24e-10
String
String values are sequences of characters that represent text. Each string is a series of Unicode characters enclosed in double quotes.
Example
"Blossom"
"Hello"
"123"
None
None is a special type that represents the absence of a value.
Example
None
Collections
Blossom provides several fundamental data structures for organizing and manipulating data.
As with all values in Blossom, collections are immutable. Consequently, operations on collections result in the creation of new collections, unlike the in-place modification common in other languages.
List
Lists are homogeneous collections of elements.
Example
Numbers: List<Int> = [1, 2, 3, 4, 5]
Set
Sets are homogeneous collections of unique elements.
Example
UniqueColors: Set<String> = {"Red", "Green", "Blue"}
ExtendedColors: Set<String> = Set.Add(UniqueColors, "Yellow")
HasRed: Bool = Set.Contains(UniqueColors, "Red")
Map
Maps are collections of key-value pairs where each key is unique.
Example
Scores: Map<String, Int> = [
"Alice" => 95,
"Bob" => 87,
"Charlie" => 92
]
Tuple
Tuples are fixed-size, heterogeneous, ordered collections of values.
Example
Point := (Float, Float)
Origin: Point = (0.0, 0.0)
Record
Records are immutable data structures comprised of named fields, each associated with a value.
Example
Person := { Name: String, Age: Int }
alice: Person = { Name: "Alice", Age: 30 }
bob: Person = { Name: "Bob", Age: 42 }
Optional
Optional types explicitly handle the presence or absence of a value.
Example
FindUser :: (id: String) : Optional<User> -> {
match Database.Query(id) -> {
user: User => user
_ => None
}
}
Composition
Blossom provides a type composition mechanism, enabling the construction of new types from primitive and collection types. Types are defined using the :=
operator, a composite symbol representing both type annotation and value assignment.
Example
ListOfFloats := List<Float>
Subject := @(Calculus, History, Biology)
Student := { Name: String, Enrollments: List<Course> }
Teacher := { Name: String, Subject: Subject }
Course := (Teacher, List<Student>)
WARNING
Composite types must be defined outside of function signatures.
This ensures code clarity and promotes type reuse. Type definitions cannot be created inline within function parameters.
Constraints
Blossom allows you to define runtime constraints on types using the &>
operator. Constraints are expressions that must evaluate to a Bool
value. Every time a value is assigned to a constrained type, its constraints are validated in order. If any constraint evaluates to False
, a @ConstraintError
is thrown.
Constraints are validated in the order they are defined.
The type name is used within each constraint block to reference the value being validated.
Example
Email
Email := String
&> String.NotEmpty(Email)
&> String.Length(Email) <= 255
&> Regex.Validate(Email, EMAIL_REGEX)
Username
Username := String
&> String.NotEmpty(Username)
&> String.Length(Username) >= 3
&> String.Length(Username) <= 20
&> Regex.OnlyAllows(Username, ~"[a-zA-Z0-9_-]")
Password
Password := String
&> String.Length(Password) >= 8
&> String.HasUpperCase(Password)
&> String.HasLowerCase(Password)
&> String.HasNumber(Password)
&> String.HasSpecialChar(Password)
Rectangle
Rectangle := { Width: Float, Height: Float }
&> Rectangle.Width > 0
&> Rectangle.Height > 0
&> Rectangle.Width <= 1000
&> Rectangle.Height <= 1000
NonEmptyList
NonEmptyList := List<T>
&> List.Length(NonEmptyList) > 0
&> List.Length(NonEmptyList) <= 1000