Update series definition to infer schema

The `series` function no longer requires an explicit schema. The element
type is now inferred at compile time from subsequent `push` calls using
Hindley-Milner type inference. This simplifies series creation and
aligns with the project's goal of making the untyped AST simple while
the system handles complexity.
This commit is contained in:
2026-03-28 14:05:20 +01:00
parent d7d1aef8ed
commit 9c85fb605b
2 changed files with 12 additions and 12 deletions
+6 -6
View File
@@ -92,8 +92,8 @@ This document defines the syntax (BNF) and semantics of the Myc language, a Lisp
- **Vectors (Tuples):** Enclosed in square brackets `[1 2 3]`. Evaluates to a vector/tuple structure.
- **Records:** Enclosed in curly braces with keyword keys and any expression as values `{:id 101 :name "Alice"}`. They provide O(1) field access using internal memory layouts. Structural equality `(= {:a 1} {:a 1})` is `true`.
- **Series:** A core concept for financial analysis. Series are "infinite" queues with a maximum length (lookback).
- They are created via the `(series lookback_limit schema)` function. The `schema` can be a record layout (e.g., `{:price :float}`) or a type keyword (e.g., `:float`).
- Example: `(series 100 {:price :float :volume :int})`.
- They are created via `(series lookback_limit)`. The element type is **inferred at compile time** from subsequent `push` calls (Hindley-Milner type inference) — no explicit schema is needed.
- Example: `(series 100)` — the compiler infers the element type from usage.
- **Indexing:** You access items in a series by calling it or an extracted field like a function with an integer index: `(my_series 0)`. **Crucially, index `0` represents the most recently pushed item.** Index `1` is the second most recent, and so on (lookback indexing).
## 4. Special Forms and Evaluation logic
@@ -137,7 +137,7 @@ The Myc runtime environment provides a collection of built-in functions and macr
- `now`: Returns the current timestamp.
### Series & Streaming
- `series`: Creates a new series with a defined layout.
- `series`: Creates a new series. Takes a lookback limit; the element type is inferred at compile time from `push` calls.
- `push`: Pushes a new item into a series.
- `len`: Returns the current number of elements in a series.
- `create-random-ohlc`: Generates a mock Open-High-Low-Close data stream.
@@ -160,8 +160,8 @@ The Myc runtime environment provides a collection of built-in functions and macr
**Financial Pipelines and Series (with Indexing):**
```clojure
(do
;; Create a series with a typed layout (Struct-of-Arrays under the hood)
(def my_ticks (series {:price :float :volume :int :msg :text}))
;; No schema needed — the compiler infers the element type from push calls.
(def my_ticks (series 100))
(push my_ticks {:price 10.5 :volume 100 :msg "A"})
(push my_ticks {:price 11.2 :volume 200 :msg "B"})
@@ -296,7 +296,7 @@ user.name
**Series with while Loop:**
```clojure
(do
(def ticks (series {:price :float}))
(def ticks (series 100))
(def i 0)
;; Push 5 prices into the series
+5 -5
View File
@@ -64,14 +64,14 @@ pub fn register(env: &Environment) {
let final_layout = RecordLayout::get_or_create(fields);
Value::Series(Rc::new(RecordSeries::new(final_layout, lookback_limit)))
}
_ => panic!("series expects a lookback_limit and a type keyword (:float, :int...) or a schema record (e.g., {{:price :float}})"),
_ => panic!("series: second arg must be a type keyword (:float, :int) or a schema record ({{:field :type}})"),
}
},
).doc("Creates a new series (ring buffer) with a defined layout and lookback limit.")
.description("First arg is the lookback limit (int), second is either a type keyword (:float, :int, :bool, :text, :datetime) or a schema record ({:field :type}).")
).doc("Creates a new series (ring buffer) with the given lookback limit.")
.description("Takes a single lookback limit (int). The element type is inferred at compile time from subsequent push calls via HM type inference. The compiler injects a second schema argument automatically; the runtime also accepts an explicit schema for cases where inference is not possible.")
.examples(&[
"(series 100 :float)",
"(series 200 {:price :float :volume :int})",
"(series 100)",
"(do (def s (series 5)) (push s 1.5) (s 0))",
]);
// (push series value) -> Void