Rayforce ← Back to home
GitHub

Type Casting & Coercion

Explicit type conversion with as, implicit coercion rules, string and temporal parsing, null propagation, and choosing between symbols and strings.

1. The as Function

The as function performs explicit type conversion. It takes a type symbol and a value, returning the value cast to that type.

(as 'type-symbol value)

Supported type symbols:

SymbolTypeDescription
'i1616-bit integerShort integer
'i3232-bit integerStandard integer
'i6464-bit integerLong integer (default)
'u8unsigned 8-bitByte values
'f6464-bit floatDouble precision (default)
'b8Booleantrue / false
'strStringVariable-length text
'symSymbolDictionary-encoded string
'dateDateDays since 2000-01-01
'timeTimeNanoseconds since midnight
'timestampTimestampNanoseconds since 2000-01-01T00:00:00
'guidGUID16-byte unique identifier

Numeric casts:

; Integer to float
(as 'f64 42)          ; 42.0

; Float to integer (truncates)
(as 'i64 3.14)        ; 3

; Narrow to wide integer
(as 'i16 100)         ; 100i16

Boolean casts:

; Integer to boolean
(as 'b8 1)          ; true
(as 'b8 0)          ; false

; Boolean to integer
(as 'i64 true)        ; 1
(as 'i64 false)       ; 0

String and symbol casts:

; String to symbol
(as 'sym "hello")    ; 'hello

; Symbol to string
(as 'str 'hello)     ; "hello"

Temporal casts from strings:

; Parse date from ISO string
(as 'date "2024.01.15")         ; 2024.01.15

; Parse time from string
(as 'time "12:30:00")           ; 12:30:00.000

; Parse timestamp
(as 'timestamp "2024-01-15T12:30:00.000")  ; 2024.01.15T12:30:00.000

Vector casts: as maps element-wise over vectors.

; Cast an integer vector to float
(as 'f64 [1 2 3])     ; [1.0 2.0 3.0]

; Cast a float vector to integer
(as 'i64 [1.5 2.7 3.9])  ; [1 2 3]

2. The type Function

The type function returns the type name of a value as a symbol. For vectors, it returns the element type.

(type 42)                ; 'i64
(type 3.14)              ; 'f64
(type "hello")           ; 'str
(type 'hello)            ; 'symbol
(type true)               ; 'b8
(type [1 2 3])           ; 'I64  (uppercase = vector type)
(type [1.0 2.0])         ; 'F64
(type 2024.01.15)        ; 'date
(type 12:30:00.000)      ; 'time
Note: type returns lowercase for atoms ('i64, 'f64, 'symbol, 'b8) and uppercase for vectors ('I64, 'F64, 'SYMBOL). The cast function as accepts both 'sym and 'symbol.

For tables, type returns 'TABLE:

(type (table [a] (list [1])))  ; 'TABLE

Use type to branch on value types at runtime or to validate input before casting.

3. Implicit Coercion Rules

Rayfall performs automatic type promotion in certain contexts. These coercions happen silently — no explicit as needed.

Coercion rules:
  • i64 + f64 → f64: Arithmetic between integers and floats promotes to float.
  • bool → i64: Booleans coerce to 1/0 in arithmetic contexts.
  • i64 → bool: In boolean contexts (if, cond), 0 is false, non-zero is true.
  • Narrower → wider: Comparisons across numeric types promote the narrower operand.
  • No string coercion: Strings never implicitly convert to numbers. Use as explicitly.
; Integer + float promotes to float
(+ 1 2.0)               ; 3.0
(* 3 1.5)               ; 4.5

; Boolean in arithmetic
(+ 10 true)              ; 11
(sum [true false true])   ; 2

; Boolean context
(if 42 "yes" "no")      ; "yes"
(if 0 "yes" "no")       ; "no"

; Cross-type comparison
(> 5 3.0)               ; true  (i64 promoted to f64)

; String + number: ERROR (no implicit coercion)
; (+ "42" 1)  ; type error — use (+ (as 'i64 "42") 1)

4. String ↔ Type Conversions

Strings can be parsed into numeric, temporal, and symbol types with as. Conversely, any value can be cast to a string.

; Parse integers and floats from strings
(as 'i64 "42")          ; 42
(as 'f64 "3.14")        ; 3.14

; Number to string
(as 'str 42)            ; "42"
(as 'str 3.14)          ; "3.14"

; Parse date from ISO string (YYYY-MM-DD)
(as 'date "2024.01.15")          ; 2024.01.15

; Parse time (HH:MM:SS.mmm)
(as 'time "12:30:00.000")        ; 12:30:00.000

; Parse timestamp (ISO 8601)
(as 'timestamp "2024-01-15T12:30:00.000")  ; 2024.01.15T12:30:00.000

; Boolean to/from string
(as 'str true)           ; "true"
(as 'b8 "true")       ; true  (non-empty string → true)
(as 'b8 "")           ; false (empty string → false)

5. Null Semantics in Casting

Null values propagate through casts. A typed null in one type becomes a typed null in the target type. Invalid string parses raise a domain error.

; Casting a typed null produces a typed null
(as 'f64 0Ni)           ; 0Nf  (null i32 → null f64)
(as 'i64 0Nf)           ; 0Nl  (null f64 → null i64)

; Null to string
(as 'str 0Ni)           ; null string

; Invalid string parse → domain error
(as 'i64 "not-a-number")  ; error: domain
(as 'date "bad-date")     ; error: domain
Null literals by type:
  • 0Ni — null i32
  • 0Nl — null i64
  • 0Nh — null i16
  • 0Ns — null sym
  • 0Nf — null f64
  • 0Nd — null date
  • 0Nt — null time
  • 0Np — null timestamp

Use nil? to test for null values: (nil? 0Ni) returns true.

; Null propagation through arithmetic
(+ 0Ni 10)              ; 0Ni  (null propagates)
(* 0Nf 2.0)             ; 0Nf

; Null in vectors — null bitmap elements stay null
(+ [1 0Ni 3] 10)          ; [11 0Ni 13]

6. Symbol vs String

Rayforce has two string representations with different performance characteristics. Choosing the right one matters for large datasets.

FeatureRAY_SYM ('sym)RAY_STR ('str)
StorageDictionary-encoded (integer indices into global intern table)Variable-length (inline for 12 bytes or fewer, pooled for longer)
ComparisonInteger comparison (fast)Byte comparison with 4-byte prefix rejection
Best forCategorical data with repeated values (status, country, product)Unique or freeform text (names, descriptions, URLs)
Group-by speedVery fast (integer hashing)Slower (string hashing)
MemoryLow for high-repeat columns (one copy per unique value)Proportional to total text length
; Creating symbol and string values
(set s 'Active)         ; symbol literal
(set t "hello world")   ; string literal

; Converting between them
(as 'sym "Active")     ; 'Active
(as 'str 'Active)      ; "Active"

; Symbol vectors (common in tables)
(set statuses ['Active 'Inactive 'Active 'Pending])
(type statuses)          ; 'SYMBOL  (uppercase = vector)
Rule of thumb: Use 'sym for columns where you expect many repeated values (status codes, categories, tickers). Use 'str for freeform text (descriptions, addresses, log messages).

7. Date/Time Types

Rayforce has three temporal types, all stored as integers internally:

TypeInternalEpochLiteral Format
datei32Days since 2000-01-012024.01.15
timei64Nanoseconds since midnight12:30:00.000
timestampi64Nanoseconds since 2000-01-01T00:00:002024.01.15T12:30:00.000

Date arithmetic:

; Add days to a date
(+ 2024.01.15 1)        ; 2024.01.16
(+ 2024.01.15 30)       ; 2024.02.14

; Subtract dates to get days between
(- 2024.01.15 2024.01.01)  ; 14

; Create a date range
(+ 2024.01.01 (til 7))   ; [2024.01.01 2024.01.02 ... 2024.01.07]

Casting between temporal types:

; Date to timestamp (midnight)
(as 'timestamp 2024.01.15)  ; 2024.01.15T00:00:00.000

; Timestamp to date (strips time)
(as 'date 2024.01.15T12:30:00.000)  ; 2024.01.15

; Date to integer (days since epoch)
(as 'i64 2024.01.15)      ; 8780

8. GUID Type

GUIDs are 16-byte unique identifiers displayed as hex strings. They are useful for primary keys, correlation IDs, and distributed systems.

; Generate a random GUID
(guid 0)
; 8c6b8861-81dc-8e36-22a0-c5b1e9c0484e  (random each time)

; GUIDs support equality comparison
(set g (guid 0))
(== g g)                  ; true

; Type check
(type (guid 0))           ; 'guid

9. sym-name Function

The sym-name function resolves integer symbol IDs into symbols. Given a symbol atom, it returns the symbol unchanged. Given an i64 (a raw sym table index), it returns the corresponding symbol.

(sym-name 'hello)        ; 'hello  (passthrough)
(sym-name 0)              ; resolves sym ID 0 to its symbol

; Useful for building dynamic strings from symbols
;; To get a string from a symbol, use cast:
(as 'str 'hello)          ; "hello"

10. Practical Examples

CSV Type Fixing

After loading a CSV, columns often arrive as strings. Cast them to proper types:

; Load CSV (all columns as strings by default)
(set raw (.csv.read "trades.csv"))

; Cast columns to proper types
(set trades
  (update {from: raw
    cols: {price:  (as 'f64 price)
           qty:    (as 'i64 qty)
           date:   (as 'date date)
           sym:    (as 'sym sym)}}))

Date Arithmetic for Analytics

; Find records in the last 30 days
(set today 2024.03.15)
(set cutoff (- today 30))

(select {from: trades where: (>= date cutoff)})

Building Typed Vectors

; Build a date vector from strings
(set dates (as 'date ["2024.01.01" "2024.02.01" "2024.03.01"]))
; [2024.01.01 2024.02.01 2024.03.01]

; Build a symbol vector from strings
(set categories (as 'sym ["A" "B" "A" "C"]))
; ['A 'B 'A 'C]

Type-Safe Table Construction

; Construct a well-typed table
(set orders (table
  [id product price date]
  (list
    [1 2 3]
    ['Widget 'Gadget 'Widget]
    [9.99 24.50 9.99]
    (+ 2024.01.01 [0 1 2]))))

; Verify column types
(meta orders)
; Shows: id (i64), product (sym), price (f64), date (date)

Next Steps