Types
Numbers, text and truth; values that might be missing; converting between types; and naming your own.
Every value in OSL has a type, and the compiler knows most of them before the program runs. That's how it catches int score = "ten" without running anything. This lesson tours the types you'll meet every day and how to move values between them.
Whole numbers and decimals#
int holds whole numbers and number holds numbers with a fractional part. A literal with a decimal point is a number; one without is an int:
int apples = 7
number price = 0.35
log apples * price
log 7 / 2
log floor(7 / 2)
log 7 % 2
log 2 ^ 10/ always divides exactly, so 7 / 2 is 3.5. floor drops the fraction, % gives the remainder and ^ raises to a power. The first line shows the usual floating-point rounding every language has: round and friends tidy it up.
number total = 7 * 0.35
log round(total)
log total.round()
log floor(2.9) ceil(2.1) abs(-4)An int fits where a number is expected, but not the other way round, because that would silently drop the fraction. Convert on purpose with .toInt():
number average = 4.8
int stars = average.toInt()
log starsText and truth#
string is text and boolean is true or false. Comparisons produce booleans, and and or combine them, and ! flips one:
int age = 17
boolean adult = age >= 18
log adult
log !adult
log age > 12 and age < 20Strings have a lesson of their own, next.
Values that might be missing#
A variable of type string always holds a string. When a value might be absent, add ? to the type, and it can also hold null:
string? nickname = null
log nickname
nickname = "Ace"
log nickname?? gives a fallback for null:
string? nickname = null
log "Hello, " ++ (nickname ?? "friend")The compiler keeps track of what could be null and stops you using it as if it couldn't. After you check for null, it knows the value is there:
def shout(string? text) string (
if text == null return "(nothing)"
return text.toUpper()
)
log shout("hi")
log shout(null)Converting between types#
Every value has methods that turn it into another type. They're how text from a file, a form or a terminal becomes a number you can do sums with:
string typed = "42"
int n = typed.toInt()
log n + 1
log (n * 2).toStr() ++ "!"
log "3.5".toNum() * 2
log "true".toBool() "yes".toBool().toInt() and .toNum() give 0 for text that isn't a number, and .toBool() is true only for the text "true". When you need to know whether the conversion worked, use .tryAs<T>, which you'll meet with errors.
When you don't know the type#
any holds a value of any type, which is what you get from parsed JSON or from code that really can be handed anything. typeof names a value's type, and the compiler narrows the value inside the check:
def describe(any value) string (
if typeof(value) == "string" return "text: " ++ value.toUpper()
if typeof(value) == "int" return "a whole number"
return "a " ++ typeof(value)
)
log describe("hi")
log describe(3)
log describe(2.5)
log describe([1, 2])Prefer real types where you can. Code written with any gives the compiler less to check and runs slower, because every operation has to look at the value first.
Unions and names for types#
A union accepts any of several types, written with |. type gives a type a name, which keeps long or repeated types readable:
type Id = string | int
def show(Id id) string -> "#" ++ id.toStr()
log show(7)
log show("a1")You'll build bigger types of your own, with fields and methods, in structs and classes.
What does log 9 / 2 print?
string? name holds null. Which line gives "anonymous"?
Average score
The scores arrive as text, and adding them with + joins the text instead of adding the numbers. Print their average, rounded to a whole number.
82