### PureLogic Quick Example: Account Purchase Logic Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/getting-started.md Demonstrates buying items with an account balance and configuration. It shows how to read configuration, get account state, fail on insufficient balance, update account state, and write success messages. Requires `Logic.run` with initial state and configuration. ```scala import purelogic.* case class Account(balance: Int) case class Config(price: Int) def buy(quantity: Int) = Logic.run(state = Account(50), reader = Config(10)) { val price = read(_.price) * quantity val balance = get(_.balance) if (balance < price) fail("Insufficient balance") set(Account(balance - price)) write("Purchase successful") } println(buy(2)) // (Vector(Purchase successful),Right((Account(30),()))) println(buy(10)) // (Vector(),Left(Insufficient balance)) ``` -------------------------------- ### Use Root-Level State Functions Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/capabilities.md Recommended for most code due to conciseness and natural readability. Functions like get and set are directly available after importing purelogic.*. ```scala import purelogic.* def program(using State[Account]): Unit = { val balance = get(_.balance) set(Account(balance + 100)) } ``` -------------------------------- ### Get Old State and Set New Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/state.md Illustrates retrieving the old state value before replacing it with a new one. ```scala val oldState: Counter = getAndSet(Counter(0)) ``` -------------------------------- ### Get Old State and Modify Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/state.md Shows how to retrieve the old state value and then modify the state. ```scala val oldState: Counter = getAndUpdate(c => Counter(c.value + 1)) ``` -------------------------------- ### Get Current State Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/state.md Shows how to retrieve the current state value. ```scala val counter: Counter = get ``` -------------------------------- ### Run Event Sourcing Program Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/event-sourcing.md Runs an event-sourced program using `Logic.runEventSourcing`. It returns `Right((events, finalState, result))` on success or `Left(error)` on failure. The example shows a sequence of deposit and withdrawal operations. ```scala val result: Either[String, (Vector[AccountEvent], Account, Unit)] = Logic.runEventSourcing(Account(100), config) { deposit(50) withdraw(30) } // Right((Vector(Deposited(50), Withdrawn(30)), Account(120), ())) ``` -------------------------------- ### Update State and Get New Value Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/state.md Demonstrates updating the state and returning the newly updated state value. ```scala val newState: Counter = updateAndGet(c => Counter(c.value + 1)) ``` -------------------------------- ### Get State with Projection Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/state.md Demonstrates extracting a specific field from the state using a projection function. ```scala val value: Int = get(_.value) ``` -------------------------------- ### Narrowing Reader Scope with Focus Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/reader.md Explains how `focus` narrows the Reader's context to a specific part, useful when a function only requires a subset of the configuration. The example shows focusing on `maxRetries` for a `retryLogic` function. ```scala def retryLogic(using Reader[Int]): Unit = { val maxRetries = read // ... } def process(using Reader[Config]): Unit = { focus(_.maxRetries) { retryLogic // only sees an Int, not the full Config } ``` -------------------------------- ### Extracting Option Value or Failing Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/abort.md Use `extractOption` to get the value from an `Option`. If the `Option` is `None`, the computation aborts with the specified error message. ```scala val user: User = extractOption(findUser(id), s"User $id not found") ``` -------------------------------- ### Extracting Either Right Value or Failing Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/abort.md Use `extractEither` to get the `Right` value from an `Either`. If the `Either` is `Left`, the computation aborts with the `Left` value. ```scala val result: Int = extractEither(someEither) ``` -------------------------------- ### Extracting Try Value or Failing with Throwable Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/abort.md Use `Abort.extractTry` to get the value from a `Try`. If the `Try` is a `Failure`, the computation aborts with the `Throwable`. Requires an `Abort[Throwable]` context. ```scala val result: Int = Abort.extractTry(scala.util.Try(someOperation())) ``` -------------------------------- ### Clearing Writer Logs Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/writer.md Clears all previously accumulated values in the Writer log. Subsequent writes will start from an empty log. Use with caution as it discards existing data. ```scala write("this will be gone") clear write("fresh start") // only "fresh start" will be in the log ``` -------------------------------- ### Running a Program with State and Reader (Scala) Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/getting-started.md Demonstrates how to provide initial `User` and `Config` objects to a program using nested `State.apply` and `Reader.apply`. ```scala val (updatedUser, result) = State(initialUser) { Reader(Config(10)) { doSomething(a) } } ``` -------------------------------- ### Running a Program with Reader (Scala) Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/getting-started.md Shows how to provide the `Config` object to a program by wrapping it with `Reader.apply`. ```scala val result = Reader(Config(10)) { doSomething(a) } ``` -------------------------------- ### Basic Reader Usage in Scala Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/reader.md Demonstrates how to define a configuration case class, a function that requires a Reader context, and how to run the function with a specific configuration value. Imports `purelogic.*`. ```scala import purelogic.* case class Config(maxRetries: Int, timeout: Int) def process(using Reader[Config]): String = { val retries = read(_.maxRetries) s"Will retry $retries times" } val result = Reader(Config(maxRetries = 3, timeout = 5000)) { process } // result: "Will retry 3 times" ``` -------------------------------- ### Convenience Runner for All Four Capabilities Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/capabilities.md Logic.run simplifies running programs that use all four capabilities by providing a single entry point for configuration and initial state. ```scala val (logs, result) = Logic.run(state = initialAccount, reader = config) { myProgram } // result: Either[AppError, (Account, Unit)] // logs: Vector[Event] ``` -------------------------------- ### Explicit Parameter Passing (Scala) Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/getting-started.md Illustrates the traditional approach of passing configuration and user objects as explicit parameters to functions, which can become cumbersome. ```scala def doSomething(a: A, config: Config): Result = ??? def validateSomething(b: B, config: Config): Unit = ??? def updateSomething(c: C, config: Config): Result = ??? ``` ```scala def doSomething(a: A, config: Config, user: User): (Result, User) = ??? def validateSomething(b: B, config: Config): Unit = ??? def updateSomething(c: C, config: Config, user: User): (Result, User) = ??? ``` -------------------------------- ### Import PureLogic Library Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/getting-started.md For most of the library's functionality, a single import is sufficient. ```scala import purelogic._ ``` -------------------------------- ### Context Functions for Implicit Parameters (Scala) Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/getting-started.md Demonstrates the Scala 3 context function syntax for defining functions that accept implicit parameters. ```scala def doSomething(a: A): Reader[Config] ?=> Result = ??? ``` -------------------------------- ### Implicit Parameters with Config and State (Scala) Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/getting-started.md Illustrates how to define functions that implicitly require both `Reader[Config]` and `State[User]` capabilities. ```scala def doSomething(a: A)(using Reader[Config], State[User]): Result = ??? // or def doSomething(a: A): (Reader[Config], State[User]) ?=> Result = ??? // or with `type Program[A] = (Reader[Config], State[User]) ?=> A` def doSomething(a: A): Program[Result] = ??? ``` -------------------------------- ### Using Multiple State and Reader Capabilities Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/faq.md Demonstrates how to use multiple capabilities of different types within a program. Ensure type parameters differ to avoid ambiguity. ```scala def program(using State[Account], State[Cart], Reader[Config]): Unit = { val balance = get[Account](_.balance) val items = get[Cart](_.items) // ... } ``` -------------------------------- ### Implicit Config Parameter with Reader (Scala) Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/getting-started.md Shows how to use `Reader[Config]` for implicit access to configuration, reducing the need for explicit parameter passing. ```scala def doSomething(a: A)(using Reader[Config]): Result = ??? ``` -------------------------------- ### Basic Writer Usage Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/writer.md Demonstrates accumulating string logs during a computation that returns an integer. The Writer context must be available. ```scala import purelogic.* def process(using Writer[String]): Int = { write("Starting") write("Processing") 42 } val (logs, result) = Writer { process } // logs: Vector("Starting", "Processing") // result: 42 ``` -------------------------------- ### Run Program with Nested Capabilities Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/capabilities.md Demonstrates nesting individual capability apply methods (Reader, Writer, State, Abort) to run a program and retrieve its results, accumulated logs, and final state. ```scala val (logs, result) = Reader(config) { Writer { State(initialAccount) { Abort { myProgram } } } } // result: Either[AppError, (Account, Unit)] // logs: Vector[Event] ``` -------------------------------- ### Logic.run Equivalence (Scala) Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/getting-started.md Explains that `Logic.run` is a convenience function equivalent to a specific nesting of `Reader`, `Writer`, `State`, and `Abort` wrappers. ```scala Logic.run(initialState, reader)(f) // is equivalent to Reader(reader)(Writer(Abort(State(initialState)(f)))) ``` -------------------------------- ### Basic Abort Usage with Division Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/abort.md Demonstrates how to define a function that can abort with a String error and how to run it within an Abort context to capture the result as an Either. ```scala import purelogic.* def divide(a: Int, b: Int)(using Abort[String]): Int = { if (b == 0) fail("Division by zero") a / b } val result: Either[String, Int] = Abort { divide(10, 0) } // result: Left("Division by zero") ``` -------------------------------- ### Reading Current Reader Value Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/reader.md Shows how to retrieve the entire configuration object from the current Reader context. ```scala val config: Config = read ``` -------------------------------- ### Running a Reader Program Directly Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/reader.md Shows the `Reader(value)(body)` constructor for providing a value and executing a body, returning the result directly without any wrapping. ```scala val result: A = Reader(myConfig) { myProgram } ``` -------------------------------- ### Basic State Usage Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/state.md Illustrates basic state management by defining a Counter, an increment function, and running multiple increments within a State computation. ```scala import purelogic.* case class Counter(value: Int) def increment(using State[Counter]): Unit = { val current = get(_.value) set(Counter(current + 1)) } val (finalState, result) = State(Counter(0)) { increment increment increment get } // finalState: Counter(3) // result: Counter(3) ``` -------------------------------- ### Use Companion Object State Functions Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/capabilities.md An alternative to root-level functions, useful for clarity when multiple capabilities are in scope. This approach calls functions directly on the capability's companion object. ```scala def program(using State[Account]): Unit = { val balance = State.get(_.balance) State.set(Account(balance + 100)) } ``` -------------------------------- ### Use Named Capability Instances Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/capabilities.md Essential when dealing with multiple capabilities of the same type or for improved readability with several distinct capabilities. Allows calling methods directly on named capability instances. ```scala def program(using account: State[Account], cart: State[Cart]): Unit = { val balance = account.get(_.balance) val items = cart.get(_.items) account.set(Account(balance - 100)) cart.set(Cart(items :+ newItem)) } ``` -------------------------------- ### Simulate Sub-Program Execution Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/capabilities.md Logic.simulateWith allows running a sub-program in isolation with custom state and reader values, without affecting the outer scope's state or accumulated writes. Errors are still propagated. ```scala // Run with custom state and reader, without affecting the outer state or writes val result = Logic.simulateWith(mockState = Account(100), mockEnv = Config(10)) { myProgram } ``` -------------------------------- ### Focus State for Subsets Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/state.md Shows how to operate on a subset of the state, where changes to the subset are reflected in the outer state. ```scala case class App(counter: Counter, name: String) def increment(using State[Counter]): Unit = { update(c => Counter(c.value + 1)) } def process(using State[App]): Unit = { focusState(_.counter)((app, c) => app.copy(counter = c)) { increment // operates on Counter, but updates App } } ``` -------------------------------- ### Replace State Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/state.md Illustrates how to replace the current state with a new value. ```scala set(Counter(10)) ``` -------------------------------- ### Running State Computation Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/state.md Provides the general signature for running a State computation, returning the final state and the computation's result. ```scala val (finalState: S, result: A) = State(initialState) { myProgram } ``` -------------------------------- ### Add PureLogic Dependency to build.sbt Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/getting-started.md Add this dependency to your build.sbt file. Use `%%` for Scala JVM and `%%%` for Scala.js and Scala Native. ```scala libraryDependencies += "com.github.ghostdogpr" %% "purelogic" % "0.2.0" ``` -------------------------------- ### Define Account Transition Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/event-sourcing.md Defines how an AccountEvent modifies the Account state. Use `update` to modify state and `ensure` to validate conditions before state changes. Failures are handled via `Abort`. ```scala import purelogic.* case class Account(balance: Int) enum AccountEvent { case Deposited(amount: Int) case Withdrawn(amount: Int) } given EventSourcing.Transition[AccountEvent, Account, String] with { def run(ev: AccountEvent): (State[Account], Abort[String]) ?=> Unit = ev match { case AccountEvent.Deposited(amount) => update(a => Account(a.balance + amount)) case AccountEvent.Withdrawn(amount) => ensure(get.balance >= amount, "Insufficient balance") update(a => Account(a.balance - amount)) } } ``` -------------------------------- ### Monadic vs. Direct Style Code Comparison Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/why-purelogic.md Compares the monadic style using ZPure with PureLogic's direct style for sequencing operations. The direct style avoids for-comprehensions and monadic syntax, using standard Scala control flow. ```scala import zio.ZPure val n = 10 // Monadic style (ZPure) ZPure .foreachDiscard(0 until n) { _ => for { r <- ZPure.service[Int, Int] s <- ZPure.get[Int] next = s + r + 1 _ <- ZPure.set(next) _ <- ZPure.log(next) } yield () } .flatMap(_ => ZPure.get[Int]) // Direct style (PureLogic) (0 until n).foreach { _ => val next = get + read + 1 set(next) write(next) } get ``` -------------------------------- ### Compute Return Value and New State Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/state.md Demonstrates computing both a return value and a new state from the current state in a single operation. ```scala val previous: Counter = modify { c => (c, Counter(c.value + 1)) } ``` -------------------------------- ### Run Infallible Event Sourcing Program Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/event-sourcing.md Use `Logic.runEventSourcingInfallible` for programs guaranteed not to fail, avoiding the `Either` wrapper. It directly returns the events, final state, and result. ```scala val (events, finalState, result) = Logic.runEventSourcingInfallible(Account(100), config) { myInfallibleProgram } ``` -------------------------------- ### State Accessor Functions Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/state.md Demonstrates read-only, write-only, and read-write access to state using StateReader, StateWriter, and State traits. ```scala import purelogic.* def readOnly(using StateReader[Counter]): Int = get(_.value) def writeOnly(using StateWriter[Counter]): Unit = set(Counter(0)) def readWrite(using State[Counter]): Int = { val v = get(_.value) set(Counter(v + 1)) v } ``` -------------------------------- ### Syntax Extension: orFail for Option Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/abort.md The `.orFail` extension method on `Option` converts `None` to an abort with a specified error message. ```scala import purelogic.syntax.* // Option[A] => A (or fail) val user: User = findUser(id).orFail(s"User $id not found") ``` -------------------------------- ### Running a Writer Computation Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/writer.md Executes the provided body within a Writer context, returning a tuple containing the accumulated values (logs) and the computation's result. The type parameter W specifies the type of values being accumulated. ```scala val (logs: Vector[W], result: A) = Writer { myProgram } ``` -------------------------------- ### Syntax Extension: orFail for Try Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/abort.md The `.orFail` extension method on `Try` converts a `Failure` to an abort with the contained `Throwable`. ```scala import purelogic.syntax.* // Try[A] => A (or fail with Throwable) val value: Int = someTry.orFail ``` -------------------------------- ### Write Account Events Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/event-sourcing.md Defines functions to write events that modify the account state. These functions use `writeEvent` to apply transitions and record events. Domain logic receives specific capabilities like `Reader`, `StateReader`, `Abort`, and `EventSourcing`. ```scala type Program[A] = EventSourcingLogic[Config, AccountEvent, Account, String, A] def deposit(amount: Int): Program[Unit] = { ensure(amount <= read(_.maxDeposit), "Amount exceeds maximum deposit") writeEvent(AccountEvent.Deposited(amount)) } def withdraw(amount: Int): Program[Unit] = { ensure(amount <= read(_.maxWithdrawal), "Amount exceeds maximum withdrawal") writeEvent(AccountEvent.Withdrawn(amount)) } ``` -------------------------------- ### Syntax Extension: orFail for Either Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/abort.md The `.orFail` extension method on `Either` converts a `Left` value to an abort. ```scala import purelogic.syntax.* // Either[E, A] => A (or fail) val result: Int = someEither.orFail ``` -------------------------------- ### Local State Modification Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/state.md Illustrates running a block of code with a temporarily modified state, ensuring the original state is restored afterward. ```scala def process(using State[Counter]): Int = { set(Counter(5)) localState(c => Counter(c.value * 10)) { get(_.value) // 50 } get(_.value) // 5 } ``` -------------------------------- ### Replay Account Events Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/event-sourcing.md Rebuilds state from a persisted event log using `replayEvents`. This applies transitions without recording them, useful for state hydration. It then proceeds with further operations like `deposit`. ```scala def loadAndProcess(savedEvents: Vector[AccountEvent]): Program[Unit] = { replayEvents(savedEvents) deposit(100) } ``` -------------------------------- ### Attempting Risky Operation and Catching Throwable Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/abort.md Use `Abort.attempt` to execute a block of code and catch any `Throwable` exceptions, converting them into an `Abort` failure. Requires an `Abort[Throwable]` context. ```scala val result: Int = Abort.attempt { riskyOperation() } ``` -------------------------------- ### Running Abort Computation to Either Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/abort.md Wrap a computation within `Abort { ... }` to execute it and obtain the result as an `Either[E, A]`, where `E` is the error type and `A` is the success type. ```scala val result: Either[E, A] = Abort { myProgram } ``` -------------------------------- ### Recovering All Errors in Abort Computation Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/abort.md Use `Abort.recover` to catch all errors within a block and provide a recovery function. Writes from the failed block are rolled back. ```scala val result = Abort.recover { fail("oops") 42 }(error => -1) // result: -1 ``` -------------------------------- ### Reading Specific Field with Projection Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/reader.md Illustrates using a projection function with `read` to extract a specific field from the Reader's value. ```scala val retries: Int = read(_.maxRetries) ``` -------------------------------- ### Writing Multiple Values Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/writer.md Appends multiple values from a collection to the current Writer log. Requires an active Writer context. ```scala writeAll(List("Step 1", "Step 2", "Step 3")) ``` -------------------------------- ### Modifying Reader Value Locally Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/reader.md Demonstrates the `local` function, which temporarily modifies the Reader's value for a block of code. The original value is restored afterward. Shows reading values before, during, and after the local modification. ```scala def process(using Reader[Config]): String = { val outer = read(_.maxRetries) // 3 val inner = local(_.copy(maxRetries = 10)) { read(_.maxRetries) // 10 } val after = read(_.maxRetries) // 3 s"$outer, $inner, $after" ``` -------------------------------- ### Recovering Specific Errors with Partial Function Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/abort.md Use `Abort.recoverSome` with a partial function to catch only specific error types. Unmatched errors are re-raised. Writes from the failed block are rolled back. ```scala val result = Abort.recoverSome { fail("timeout") 42 } { case "timeout" => 0 } // "timeout" is recovered, any other error is propagated ``` -------------------------------- ### Writing a Single Value Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/writer.md Appends a single value to the current Writer log. Ensure a Writer context is active. ```scala write("Order validated") ``` -------------------------------- ### Capturing Writes in a Nested Scope Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/writer.md Runs a block within a nested Writer scope, returning both the captured writes and the block's result. Captured writes are also forwarded to the outer writer. This is useful for isolating log segments. ```scala def process(using Writer[String]): Unit = { write("before") val (captured, result) = capture { write("inside") 42 } write("after") // captured: Vector("inside") // outer log: Vector("before", "inside", "after") } ``` -------------------------------- ### Modify State Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/state.md Shows how to modify the state using a function that takes the current state and returns a new state. ```scala update(c => Counter(c.value + 1)) ``` -------------------------------- ### Ensuring a Condition is Met Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/abort.md Use `ensure` to check if a condition is true. If false, the computation aborts with the provided error message. ```scala ensure(age >= 18, "Must be an adult") ``` -------------------------------- ### Define Polymorphic Account Transitions Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/event-sourcing.md Defines separate transitions for subtypes of `AccountEvent` (e.g., `Deposited`, `Withdrawn`). `writeEvent` accepts any `Ev1 <: Ev`, allowing specific transitions for each subtype. Note: This approach works with `sealed trait` but not `enum` due to Scala's enum widening. ```scala sealed trait AccountEvent object AccountEvent { case class Deposited(amount: Int) extends AccountEvent case class Withdrawn(amount: Int) extends AccountEvent } given EventSourcing.Transition[AccountEvent.Deposited, Account, String] with { def run(ev: AccountEvent.Deposited): (State[Account], Abort[String]) ?=> Unit = update(a => Account(a.balance + ev.amount)) } given EventSourcing.Transition[AccountEvent.Withdrawn, Account, String] with { def run(ev: AccountEvent.Withdrawn): (State[Account], Abort[String]) ?=> Unit = ensure(get.balance >= ev.amount, "Insufficient balance") update(a => Account(a.balance - ev.amount)) } ``` -------------------------------- ### Type Alias for Program Capabilities (Scala) Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/getting-started.md Defines a type alias `Program[A]` for functions with a `Reader[Config]` capability, simplifying function signatures. ```scala type Program[A] = Reader[Config] ?=> A def doSomething(a: A): Program[Result] = ??? ``` -------------------------------- ### Simplify Complex Signatures with Logic Type Alias Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/capabilities.md A type alias to simplify function signatures that utilize all four core capabilities (Reader, Writer, State, Abort), making the code more concise. ```scala type Logic[R, W, S, E, A] = (Reader[R], Writer[W], State[S], Abort[E]) ?=> A def process(order: Order): Logic[Config, Event, Account, AppError, Result] = ??? type MyProgram[A] = (Reader[Config], State[Account], Abort[AppError]) ?=> A def process(order: Order): MyProgram[Result] = ??? ``` -------------------------------- ### Aborting Computation with Fail Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/abort.md Use the `fail` function to immediately abort the current computation with a specified error message. ```scala fail("Something went wrong") ``` -------------------------------- ### EventSourcingLogic Type Alias Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/event-sourcing.md A type alias for event-sourced programs, simplifying type signatures. It includes capabilities for `EventSourcing`, `Reader`, `StateReader`, and `Abort`, but not direct `State` mutation. ```scala type EventSourcingLogic[R, Ev, S, Err, A] = (EventSourcing[Ev, S, Err], Reader[R], StateReader[S], Abort[Err]) ?=> A ``` -------------------------------- ### Infallible Program Runner Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/capabilities.md Use Logic.runInfallible for programs that do not utilize the Abort capability, avoiding the Either wrapper and simplifying the return type. ```scala val (logs, finalState, result) = Logic.runInfallible(state = initialAccount, reader = config) { myInfallibleProgram } ``` -------------------------------- ### Ensuring a Condition is Not Met Source: https://github.com/ghostdogpr/purelogic/blob/master/docs/abort.md Use `ensureNot` to check if a condition is false. If true, the computation aborts with the provided error message. ```scala ensureNot(balance < 0, "Negative balance") ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.