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. - **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`. - **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). - **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`). - 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 {:price :float :volume :int})`. - 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). - **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 ## 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. - `now`: Returns the current timestamp.
### Series & Streaming ### 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. - `push`: Pushes a new item into a series.
- `len`: Returns the current number of elements in a series. - `len`: Returns the current number of elements in a series.
- `create-random-ohlc`: Generates a mock Open-High-Low-Close data stream. - `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):** **Financial Pipelines and Series (with Indexing):**
```clojure ```clojure
(do (do
;; Create a series with a typed layout (Struct-of-Arrays under the hood) ;; No schema needed — the compiler infers the element type from push calls.
(def my_ticks (series {:price :float :volume :int :msg :text})) (def my_ticks (series 100))
(push my_ticks {:price 10.5 :volume 100 :msg "A"}) (push my_ticks {:price 10.5 :volume 100 :msg "A"})
(push my_ticks {:price 11.2 :volume 200 :msg "B"}) (push my_ticks {:price 11.2 :volume 200 :msg "B"})
@@ -296,7 +296,7 @@ user.name
**Series with while Loop:** **Series with while Loop:**
```clojure ```clojure
(do (do
(def ticks (series {:price :float})) (def ticks (series 100))
(def i 0) (def i 0)
;; Push 5 prices into the series ;; 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); let final_layout = RecordLayout::get_or_create(fields);
Value::Series(Rc::new(RecordSeries::new(final_layout, lookback_limit))) 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.") ).doc("Creates a new series (ring buffer) with the given 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}).") .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(&[ .examples(&[
"(series 100 :float)", "(series 100)",
"(series 200 {:price :float :volume :int})", "(do (def s (series 5)) (push s 1.5) (s 0))",
]); ]);
// (push series value) -> Void // (push series value) -> Void