diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index ea739cd..80dadc9 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -30,7 +30,7 @@ The entire library is built around Effekt's effect system. Instead of calling fu The main effects used are: -- **`read[Event]`**: Pull-based stream input. When a component needs the next event, it performs `do read[Event]()`. A handler somewhere up the call stack provides the actual value. +- **`next[Event]`**: Pull-based stream input. When a component needs the next event, it performs `do next[Event]()`. A handler somewhere up the call stack provides the actual value. - **`emit[Event]`**: Push-based stream output. Components emit results using `do emit(event)`. Handlers decide what to do with each value. - **`AnomalyDetection`**: Interface with `anomaly()` and `noAnomaly()` operations. Anomaly detectors call these, and handlers decide how to react (log, alert, store, etc.). - **`stop`**: Used to signal the end of the stream. @@ -60,13 +60,13 @@ For example, all anomaly loggers handle the `AnomalyDetection` effect the same w Switching from console to file logging is just changing one line. The rest of the pipeline stays exactly the same. -Similarly for the rest of the library - whether you use `csvFeedPull()`, `normalDistributedPull()`, or `cpuUsagePullStream()`, they all provide events via `read[Event]`, so any aggregator works with any input source and any anomaly detector works with any aggregator. +Similarly for the rest of the library - whether you use `csvFeedPull()`, `normalDistributedPull()`, or `cpuUsagePullStream()`, they all provide events via `next[Event]`, so any aggregator works with any input source and any anomaly detector works with any aggregator. ### Stream Model The library uses two stream models: -1. **Pull streams**: The consumer requests data by performing `read[Event]`. The handler decides where the data comes from. This is lazy—no work happens until someone reads. +1. **Pull streams**: The consumer requests data by performing `next[Event]`. The handler decides where the data comes from. This is lazy—no work happens until someone reads. 2. **Push streams**: The producer emits data by performing `emit[Event]`. The handler decides what to do with each value. @@ -139,10 +139,10 @@ The library uses minimal FFI: To add your own detection strategy, write a function that reads events and calls the `AnomalyDetection` effect: ```js -def myCustomDetector(param: Double): Unit / { read[Event], AnomalyDetection } = { +def myCustomDetector(param: Double): Unit / { next[Event], AnomalyDetection } = { with boundary while (true) { - val ev = do read[Event]() + val ev = do next[Event]() // your detection logic here if (isAnomalous) { do anomaly(AnomalyEvaluation(ev, true, score)) @@ -155,11 +155,11 @@ def myCustomDetector(param: Double): Unit / { read[Event], AnomalyDetection } = ### Writing a Custom Input Source -Implement a handler for `read[Event]`: +Implement a handler for `next[Event]`: ```js -def myDataSource() { body: () => Unit / read[Event] }: Unit = { - try body() with read[Event] { +def myDataSource() { body: () => Unit / next[Event] }: Unit = { + try body() with next[Event] { val nextEvent = // fetch from somewhere resume { nextEvent } } diff --git a/README.md b/README.md index 29969f1..b54e1ea 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ You can run the examples by calling the function in `src/main.effekt` and then e ### Data Input Sources -The library provides various input sources that handle `read[Event]`. All input handlers share the same interface, so they can be swapped without changing the rest of the pipeline. +The library provides various input sources that handle `next[Event]`. All input handlers share the same interface, so they can be swapped without changing the rest of the pipeline. #### CSV Input (`src/lib/stream_input`) @@ -159,7 +159,7 @@ Usage examples are provided in the `examples` namespace within each component fi ### Aggregation Functions -All aggregators read events via `read[Event]` and emit aggregated results via `emit[Event]`. +All aggregators read events via `next[Event]` and emit aggregated results via `emit[Event]`. | Function | Description | Space | Time per event | |----------|-------------|-------|----------------| @@ -175,7 +175,7 @@ All aggregators read events via `read[Event]` and emit aggregated results via `e ### Anomaly Detection -The library provides three anomaly detection strategies (`src/lib/anomaly_detection`). Each detector reads events via `read[Event]` and calls the `AnomalyDetection` interface (`anomaly()` or `noAnomaly()`). +The library provides three anomaly detection strategies (`src/lib/anomaly_detection`). Each detector reads events via `next[Event]` and calls the `AnomalyDetection` interface (`anomaly()` or `noAnomaly()`). | Detector | Description | |----------|-------------| @@ -193,7 +193,7 @@ The library provides handlers to log events or anomaly evaluations. "Rethrow" va #### Event Logging (`src/lib/event_logger`) -Handles `emit[Event]` (push) or `read[Event]` (pull) effects. +Handles `emit[Event]` (push) or `next[Event]` (pull) effects. | Handler | Description | |---------|-------------| @@ -225,7 +225,7 @@ Usage examples are provided in the `examples` namespace within each logger file. ## Architecture Overview -The library is built around Effekt's effect system. Components communicate through effects (`read[Event]`, `emit[Event]`, `AnomalyDetection`) that get handled by outer handlers. This design enables: +The library is built around Effekt's effect system. Components communicate through effects (`next[Event]`, `emit[Event]`, `AnomalyDetection`) that get handled by outer handlers. This design enables: - **Composability**: Stack handlers to build pipelines - **Exchangeability**: Swap any handler for another without changing the rest of the pipeline diff --git a/flake.lock b/flake.lock index 8b9e908..033fc01 100644 --- a/flake.lock +++ b/flake.lock @@ -11,11 +11,11 @@ "sbt-derivation": "sbt-derivation" }, "locked": { - "lastModified": 1775459861, - "narHash": "sha256-u003wFjSJ6EAzVoikQZAtfLj0wK1vH6YQYpY2/K0c3A=", + "lastModified": 1779116722, + "narHash": "sha256-F6tiXWuWOT+s5j04j4W+x8x541ZzjjUm7HxEHTQu0Qw=", "owner": "jiribenes", "repo": "effekt-nix", - "rev": "58b5422b1c30d2c0ac92dc8cfa80d4ae8be94725", + "rev": "60af21b260a87f58da8674240084ed0fb85a4b45", "type": "github" }, "original": { @@ -44,11 +44,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1775763530, - "narHash": "sha256-BuTK9z1QEwWPOIakQ1gCN4pa4VwVJpfptYCviy2uOGc=", + "lastModified": 1780336545, + "narHash": "sha256-vhVhuXzFrIOfcssC/9hDHx7MHzDKjF3keHuREOQqQiQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b0188973b4b2a5b6bdba8b65381d6cd09a533da0", + "rev": "4df1b885d76a54e1aa1a318f8d16fd6005b6401f", "type": "github" }, "original": { diff --git a/src/lib/aggregation.effekt b/src/lib/aggregation.effekt index 6c6db26..28c7f24 100644 --- a/src/lib/aggregation.effekt +++ b/src/lib/aggregation.effekt @@ -17,11 +17,11 @@ extern def negInfinity: Double = /// Aggregate the minimum value from an unbounded stream of events /// /// O(1) space complexity, O(1) time complexity per read -def aggregateMin(): Unit / { read[Event], emit[Event] } = { +def aggregateMin(): Unit / { next[Event], emit[Event] } = { with boundary var min = infinity() while (true) { - val ev = do read[Event]() + val ev = do next[Event]() val value = ev.value if (value < min) { min = value @@ -33,14 +33,14 @@ def aggregateMin(): Unit / { read[Event], emit[Event] } = { /// Aggregate the minimum value over a sliding window of given size /// /// O(windowSize) space complexity, O(1) time complexity on average per read -def aggregateMinWindow(windowSize: Int) : Unit / { read[Event], emit[Event] } = { +def aggregateMinWindow(windowSize: Int) : Unit / { next[Event], emit[Event] } = { with boundary with on[OutOfBounds].panic() val windowArr = array[Double](windowSize, infinity()) // Array to cycle through window values var min = infinity() var index = 0 while (true) { - val ev = do read[Event]() + val ev = do next[Event]() val value = ev.value if (windowArr.get(index) == min) { // Recompute min @@ -63,11 +63,11 @@ def aggregateMinWindow(windowSize: Int) : Unit / { read[Event], emit[Event] } = /// Aggregate the maximum value from an unbounded stream of events /// /// O(1) space complexity, O(1) time complexity per read -def aggregateMax(): Unit / { read[Event], emit[Event] } = { +def aggregateMax(): Unit / { next[Event], emit[Event] } = { with boundary var max = negInfinity() while (true) { - val ev = do read[Event]() + val ev = do next[Event]() val value = ev.value if (value > max) { max = value @@ -79,14 +79,14 @@ def aggregateMax(): Unit / { read[Event], emit[Event] } = { /// Aggregate the maximum value over a sliding window of given size /// /// O(windowSize) space complexity, O(1) time complexity on average per read -def aggregateMaxWindow(windowSize: Int) : Unit / { read[Event], emit[Event] } = { +def aggregateMaxWindow(windowSize: Int) : Unit / { next[Event], emit[Event] } = { with boundary with on[OutOfBounds].panic() val windowArr = array[Double](windowSize, negInfinity()) // Array to cycle through window values var max = negInfinity() var index = 0 while (true) { - val ev = do read[Event]() + val ev = do next[Event]() val value = ev.value if (windowArr.get(index) == max) { // Recompute max -> O(windowSize) @@ -109,11 +109,11 @@ def aggregateMaxWindow(windowSize: Int) : Unit / { read[Event], emit[Event] } = /// Aggregate the mean value from an unbounded stream of events /// /// O(1) space complexity, O(1) time complexity per read -def aggregateMean(): Unit / { read[Event], emit[Event] } = { +def aggregateMean(): Unit / { next[Event], emit[Event] } = { with boundary var counter: Counter = counter::empty() while (true) { - val ev = do read[Event]() + val ev = do next[Event]() val value = ev.value counter = counter.add(value) val meanVal = counter.mean() @@ -124,14 +124,14 @@ def aggregateMean(): Unit / { read[Event], emit[Event] } = { /// Aggregate the mean value over a sliding window of given size /// /// O(windowSize) space complexity, O(1) time complexity per read -def aggregateMeanWindow(windowSize: Int): Unit / { read[Event], emit[Event] } = { +def aggregateMeanWindow(windowSize: Int): Unit / { next[Event], emit[Event] } = { with boundary with on[OutOfBounds].panic() val windowArr = array[Double](windowSize, negInfinity()) // Array to cycle through window values var counter = counter::empty() var index = 0 while (true) { - val ev = do read[Event]() + val ev = do next[Event]() val value = ev.value if (windowArr.get(index) != negInfinity()) { counter = counter.remove(windowArr.get(index)) @@ -146,13 +146,13 @@ def aggregateMeanWindow(windowSize: Int): Unit / { read[Event], emit[Event] } = /// Aggregate the median value from an unbounded stream of events /// /// O(n) space complexity, O(n log n) time complexity per read -def aggregateMedian(): Unit / { read[Event], emit[Event] } = { +def aggregateMedian(): Unit / { next[Event], emit[Event] } = { with boundary with on[OutOfBounds].panic() val windowList: List[Double] = empty[Double]() var lst = windowList while (true) { - val ev = do read[Event]() + val ev = do next[Event]() lst = Cons(ev.value, lst) val medianVal = internal::listMedian(lst) do emit(Event(ev.timestamp, medianVal)) @@ -162,12 +162,12 @@ def aggregateMedian(): Unit / { read[Event], emit[Event] } = { /// Aggregate the median value over a sliding window of given size /// /// O(windowSize) space complexity, O(n log n) time complexity per read -def aggregateMedianWindow(windowSize: Int): Unit / { read[Event], emit[Event] } = { +def aggregateMedianWindow(windowSize: Int): Unit / { next[Event], emit[Event] } = { with boundary with on[OutOfBounds].panic() var lst: List[Double] = empty[Double]() while (true) { - val ev = do read[Event]() + val ev = do next[Event]() lst = Cons(ev.value, lst) if (lst.size() > windowSize) { lst = lst.deleteAt(lst.size() - 1) // remove oldest element @@ -192,7 +192,7 @@ namespace examples { ] try { aggregateMin() - } with read[Event] { () => + } with next[Event] { () => if (lst.isEmpty()) { println("No more values to read, stopping...") resume { do stop() } @@ -219,7 +219,7 @@ namespace examples { ] try { aggregateMeanWindow(2) - } with read[Event] { () => + } with next[Event] { () => if (values.isEmpty()) { println("No further input values; shutting down...") resume { do stop() } diff --git a/src/lib/cpu_utilization_input.effekt b/src/lib/cpu_utilization_input.effekt index aa98cf0..4c57fc7 100644 --- a/src/lib/cpu_utilization_input.effekt +++ b/src/lib/cpu_utilization_input.effekt @@ -48,8 +48,8 @@ extern def getCpuUsage() at async: Double = })""" /// Infinite pull stream that yields CPU usage events -def cpuUsagePullStream() { body: () => Unit / { read[Event] } }: Unit = { - try body() with read[Event] { +def cpuUsagePullStream() { body: () => Unit / { next[Event] } }: Unit = { + try body() with next[Event] { resume { val cpuUsage = getCpuUsage() val timestamp = currentTimestampMillis() @@ -59,9 +59,9 @@ def cpuUsagePullStream() { body: () => Unit / { read[Event] } }: Unit = { } /// Pull stream that yields at most `limit` CPU usage events -def cpuUsagePullStream(limit: Int) { body: () => Unit / { read[Event] } }: Unit = { +def cpuUsagePullStream(limit: Int) { body: () => Unit / { next[Event] } }: Unit = { var count = 0 - try body() with read[Event] { + try body() with next[Event] { resume { if (count >= limit) { do stop() @@ -81,7 +81,7 @@ namespace examples { var i = 0 cpuUsagePullStream() { while (i < 10) { - println("CPU Usage Event received: " ++ do read[Event]().show()) + println("CPU Usage Event received: " ++ do next[Event]().show()) i = i + 1 time::sleep(500) } diff --git a/src/lib/csv.effekt b/src/lib/csv.effekt index d822862..766615b 100644 --- a/src/lib/csv.effekt +++ b/src/lib/csv.effekt @@ -114,9 +114,9 @@ def readUntil { shouldStop: Char => Bool }: String / Scan[Char] = def readQuotedString(): Unit / { Scan[Char], emit[Char], Exception[WrongFormat] } = { try { expect("Expected \"") { readIf('"') } - while(do read[Char]() is c and c != '"') { + while(do next[Char]() is c and c != '"') { c match { - case '\\' => do read[Char]() match { + case '\\' => do next[Char]() match { case '"' => do emit('\"') case '\\' => do emit('\\') case '/' => do emit('/') @@ -302,7 +302,7 @@ def main() = { // Empty row! val realCsv = ",name,age\n\n,John,42\n,Joe,10\n,Jolene,27" val res = feed(realCsv) { - with returning::scanner[Char, CsvValue] + with returning::peeking[Char, CsvValue] build { decodeCsv() }.second } inspect(res) diff --git a/src/lib/event_logger.effekt b/src/lib/event_logger.effekt index ce8983c..f13739e 100644 --- a/src/lib/event_logger.effekt +++ b/src/lib/event_logger.effekt @@ -28,12 +28,12 @@ def logToConsoleRethrow() {body: () => Unit / emit[Event]}: Unit / emit[Event] = } /// An AnomalyDetection handler that logs all events to the console for pull-based streams -def logToConsolePull() {body: () => Unit / read[Event]}: Unit / read[Event] = { +def logToConsolePull() {body: () => Unit / next[Event]}: Unit / next[Event] = { with boundary try { body() - } with read[Event] { - val e = do read[Event]() + } with next[Event] { + val e = do next[Event]() resume { println("Event: " ++ e.value.show() ++ " at t=" ++ e.timestamp.show()) e @@ -96,15 +96,15 @@ def logToFileCsvRethrow(filePath: String) {body: () => Unit / emit[Event]}: Unit } } -def logToFileCsvPull(filePath: String) {body: () => Unit / read[Event]}: Unit / { read[Event], Exception[IOError] } = { +def logToFileCsvPull(filePath: String) {body: () => Unit / next[Event]}: Unit / { next[Event], Exception[IOError] } = { var buffer: List[CsvRow] = [["timestamp", "value"]] var first = true val maxBufferSize = 1000 try { // Double try is required because the read in the with block throws stop when the stream ends try { body() - } with read[Event] { - val e = do read[Event]() + } with next[Event] { + val e = do next[Event]() resume { val row: CsvRow = [ e.timestamp.show(), diff --git a/src/lib/stream_input.effekt b/src/lib/stream_input.effekt index 9731c11..9ec31e6 100644 --- a/src/lib/stream_input.effekt +++ b/src/lib/stream_input.effekt @@ -12,29 +12,29 @@ import map /// Connect a push-based stream to a pull-based stream. -def pushToPullStream[T]() {pushStr: () => Unit / { emit[T] } } {pullStr: () => Unit / { read[T] } } = { +def pushToPullStream[T]() {pushStr: () => Unit / { emit[T] } } {pullStr: () => Unit / { next[T] } } = { stream::source[T] { pushStr() } { pullStr() } } -def pullStreamDelayer(delayMs: Int) {body: () => Unit / { read[Event] } }: Unit / { read[Event] } = { - try body() with read[Event] { +def pullStreamDelayer(delayMs: Int) {body: () => Unit / { next[Event] } }: Unit / { next[Event] } = { + try body() with next[Event] { resume { - val e = do read[Event]() + val e = do next[Event]() time::sleep(delayMs) e } } } -def pullStreamLimiter(maxEvents: Int) {body: () => Unit / { read[Event] } }: Unit / { read[Event] } = { +def pullStreamLimiter(maxEvents: Int) {body: () => Unit / { next[Event] } }: Unit / { next[Event] } = { var count = 0 - try body() with read[Event] { + try body() with next[Event] { if (count >= maxEvents) { resume { do stop() } } else { resume { count = count + 1 - val e = do read[Event]() + val e = do next[Event]() e } } @@ -43,7 +43,7 @@ def pullStreamLimiter(maxEvents: Int) {body: () => Unit / { read[Event] } }: Uni def csvFeed(path: String, columnNameValue: String, delayMs: Int): Unit / { emit[Event], Exception[IOError], Exception[WrongFormat] } = { readFileUTF8(path) { - returning::scanner[Char, Unit] { + returning::peeking[Char, Unit] { try { getColumnOf(columnNameValue) { decodeCsv() @@ -60,7 +60,7 @@ def csvFeed(path: String, columnNameValue: String, delayMs: Int): Unit / { emit[ def csvFeed(path: String, columnNameValue: String, columnNameTimestamp: String, delayMs: Int): Unit / { emit[Event], Exception[IOError], Exception[WrongFormat], fail } = { with on[MissingValue].panic() // should never happen readFileUTF8(path) { - returning::scanner[Char, Unit] { + returning::peeking[Char, Unit] { try { getColumnsOf([columnNameValue, columnNameTimestamp]) { decodeCsv() @@ -77,7 +77,7 @@ def csvFeed(path: String, columnNameValue: String, columnNameTimestamp: String, def csvFeedStr(csvStr: String, columnName: String, delayMs: Int): Unit / { emit[Event], Exception[WrongFormat] } = { feed(csvStr) { - returning::scanner[Char, Unit] { + returning::peeking[Char, Unit] { try { getColumnOf(columnName) { decodeCsv() @@ -94,7 +94,7 @@ def csvFeedStr(csvStr: String, columnName: String, delayMs: Int): Unit / { emit[ def csvFeedStr(csvStr: String, columnNameValue: String, columnNameTimestamp: String, delayMs: Int): Unit / { emit[Event], Exception[WrongFormat], fail } = { with on[MissingValue].panic() // should never happen feed(csvStr) { - returning::scanner[Char, Unit] { + returning::peeking[Char, Unit] { try { getColumnsOf([columnNameValue, columnNameTimestamp]) { decodeCsv() @@ -109,36 +109,36 @@ def csvFeedStr(csvStr: String, columnNameValue: String, columnNameTimestamp: Str } } -def csvFeedPull(path: String, columnNameValue: String, delayMs: Int) {body: () => Unit / { read[Event], Exception[IOError], Exception[WrongFormat] } }: Unit / { Exception[IOError], Exception[WrongFormat] } = { +def csvFeedPull(path: String, columnNameValue: String, delayMs: Int) {body: () => Unit / { next[Event], Exception[IOError], Exception[WrongFormat] } }: Unit / { Exception[IOError], Exception[WrongFormat] } = { pushToPullStream[Event]() { csvFeed(path, columnNameValue, delayMs) } { body() } } -def csvFeedPull(path: String, columnNameValue: String, columnNameTimestamp: String, delayMs: Int) {body: () => Unit / { read[Event], Exception[IOError], Exception[WrongFormat], fail } }: Unit / { Exception[IOError], Exception[WrongFormat], fail } = { +def csvFeedPull(path: String, columnNameValue: String, columnNameTimestamp: String, delayMs: Int) {body: () => Unit / { next[Event], Exception[IOError], Exception[WrongFormat], fail } }: Unit / { Exception[IOError], Exception[WrongFormat], fail } = { pushToPullStream[Event]() { csvFeed(path, columnNameValue, columnNameTimestamp, delayMs) } { body() } } -def csvFeedStrPull(csvStr: String, columnNameValue: String, delayMs: Int) {body: () => Unit / { read[Event], Exception[WrongFormat] } }: Unit / { Exception[WrongFormat] } = { +def csvFeedStrPull(csvStr: String, columnNameValue: String, delayMs: Int) {body: () => Unit / { next[Event], Exception[WrongFormat] } }: Unit / { Exception[WrongFormat] } = { pushToPullStream[Event]() { csvFeedStr(csvStr, columnNameValue, delayMs) } { body() } } -def csvFeedStrPull(csvStr: String, columnNameValue: String, columnNameTimestamp: String, delayMs: Int) {body: () => Unit / { read[Event], Exception[WrongFormat], fail } }: Unit / { Exception[WrongFormat], fail } = { +def csvFeedStrPull(csvStr: String, columnNameValue: String, columnNameTimestamp: String, delayMs: Int) {body: () => Unit / { next[Event], Exception[WrongFormat], fail } }: Unit / { Exception[WrongFormat], fail } = { pushToPullStream[Event]() { csvFeedStr(csvStr, columnNameValue, columnNameTimestamp, delayMs) } { body() } } -def normalDistributedPull(mu: Double, sigma: Double) {body: () => Unit / { read[Event] } }: Unit / { } = { - try body() with read[Event] { +def normalDistributedPull(mu: Double, sigma: Double) {body: () => Unit / { next[Event] } }: Unit / { } = { + try body() with next[Event] { resume { internal::generateNormalDistributedValue(mu, sigma).toEvent() } } } -def exponentialDistributedPull(lambda: Double) {body: () => Unit / { read[Event] } }: Unit / { } = { - try body() with read[Event] { +def exponentialDistributedPull(lambda: Double) {body: () => Unit / { next[Event] } }: Unit / { } = { + try body() with next[Event] { resume { internal::generateExponentialDistributedValue(lambda).toEvent() } } } -def uniformDistributedPull(a: Double, b: Double) {body: () => Unit / { read[Event] } }: Unit / { } = { - try body() with read[Event] { +def uniformDistributedPull(a: Double, b: Double) {body: () => Unit / { next[Event] } }: Unit / { } = { + try body() with next[Event] { resume { internal::generateUniformDistributedValue(a, b).toEvent() } } } @@ -185,7 +185,7 @@ namespace examples { csvFeedPull("src/test/testdata_with_timestamp.csv", "value", "timestamp", 500) { while (true) { println("Read new value (waiting till it comes available)...") - val v = do read[Event]() + val v = do next[Event]() println("Received value: " ++ v.show()) } } @@ -195,7 +195,7 @@ namespace examples { normalDistributedPull(0.0, 1.0) { effekt::each(1, 11) { (n) => try { - val v = do read[Event]() + val v = do next[Event]() println("Normal distributed value: " ++ v.show()) } with stop { println("This should never be called since the stream is infinite.") diff --git a/src/test/lib.effekt b/src/test/lib.effekt index 6845c14..9ccc750 100644 --- a/src/test/lib.effekt +++ b/src/test/lib.effekt @@ -29,7 +29,7 @@ def assertNoEmits[T] { program: () => Unit / emit[T] } : Unit / { Assertion } = } } -def assertReads[T](inputs: List[T]) { equals: (T, T) => Bool } { program: () => Unit / read[T] } : Unit / { Assertion } = { +def assertReads[T](inputs: List[T]) { equals: (T, T) => Bool } { program: () => Unit / next[T] } : Unit / { Assertion } = { with on[MissingValue].panic() var lst = inputs assertFalse(lst.isEmpty(), "assertReads requires a non empty list of inputs") @@ -37,7 +37,7 @@ def assertReads[T](inputs: List[T]) { equals: (T, T) => Bool } { program: () => var isStop: Bool = false try { program() - } with read[T] { () => + } with next[T] { () => lst match { case Nil() => if (isStop) { diff --git a/src/test/stream_input_test.effekt b/src/test/stream_input_test.effekt index 324e5cc..4fcec61 100644 --- a/src/test/stream_input_test.effekt +++ b/src/test/stream_input_test.effekt @@ -68,7 +68,7 @@ def testSuite() = suite("stream_input_csv") { normalDistributedPull(mu, sigma) { while (samplesRead < numSamples) { try { - val e = do read[Event]() + val e = do next[Event]() val v = e.value // Check that the value is within a reasonable range (e.g., within 5 standard deviations) if (not(v >= mu - 5.0 * sigma && v <= mu + 5.0 * sigma)) @@ -100,7 +100,7 @@ def testSuite() = suite("stream_input_csv") { exponentialDistributedPull(lambda) { while (samplesRead < numSamples) { try { - val e = do read[Event]() + val e = do next[Event]() val v = e.value assertTrue(v >= 0.0, "Exponential distributed value should be non-negative") @@ -131,7 +131,7 @@ def testSuite() = suite("stream_input_csv") { uniformDistributedPull(a, b) { while (samplesRead < numSamples) { try { - val e = do read[Event]() + val e = do next[Event]() val v = e.value if (v < a || v > b)