Typing and Constants
Klyn is statically typed from top to bottom. Type inference keeps declarations compact, but the inferred type is still a real compile-time type. This page explains the practical rules that matter when you declare variables, annotate APIs, and rely on constants.
count = 10 # inferred as Int
ratio = 0.5 # inferred as Double
name = "Klyn" # inferred as String
count = 12 # valid
count = "twelve" # invalid
Inference is convenient syntax, not dynamic typing. The compiler remembers the type and checks every later assignment against it.
count as Int = 10
price as Double = 19.99
names as ArrayList<String> = ["Ada", "Grace"]
Use explicit annotations when the type is part of the design, when a public API must stay self-documenting, or when you want a specific numeric type instead of the default literal type.
| Family | Types | Notes |
|---|---|---|
| Signed integers | Byte, SByte, Short, Int, Long |
Use Int by default unless you have a clear range or ABI reason not to. |
| Unsigned integers | UShort, UInt, ULong |
Literal suffixes such as 10u make unsigned intent explicit. |
| Floating-point | Float, Double |
Floating literals default to Double; use 0.5f for Float. |
Klyn rejects assignments that would lose precision or violate the target type range.
const
const PI = 3.141592653589793
const APP_NAME as String = "Klyn"
def demo():
const LIMIT as Int = 3
print(LIMIT)
Constants may be declared at module level or locally. They cannot be reassigned, incremented, decremented, or modified through compound assignment.
message as String = null
if message is null:
print("no message")
else:
print(message)
Reference values may be null. Use is null and
is not null for clear null checks.
names as ArrayList<String> = ["Ada", "Grace"]
config as Map<String, Int> = {"port": 8080}
values as IList<Int> = ImmutableList([1, 2, 3])
pair as Tuple<String, Int> = ("age", 42)
Generic arguments are part of the declared type. Literals often infer them automatically, but
explicit generic annotations are usually clearer in public APIs. In Klyn,
IList<T> is the immutable homogeneous collection contract, while
Tuple<...Ts> denotes a fixed positional tuple.
value = [10, 20, 30]
metadata = type(value)
print(metadata.fullName)
The full reflection API is documented later, but type(...) is already useful when
you want to verify what the compiler or runtime considers the real type of a value.
- Expecting inference to allow later assignments of unrelated types.
- Forgetting that
constalso forbids+=,++, and similar updates. - Relying on implicit numeric narrowing instead of converting explicitly.
- Leaving externally visible properties untyped because “the compiler can infer it anyway”.