Effect
Capture
Effect
Oleg Nizhnikov ^ {Evolution, Gaming}




Benefits of IO
Benefits of IO

Benefits of IO

Direct Style

Direct Style


Capture Checking

Capture Checking


AGENDA
Effects

Uniqueness

Lifetimes
Capture Checking


Sequents
STLC
Context
class Orders(customerRepo: CustomerRepo) {
  def products(order: Order): IO[List[Products]] = {
     val productIds: List[ProductIds] = order.items.flatMap(_.productIds)
     
     productIds.traverse{id : CustomerId => 
     	customerRepo.getById(id)     	
     }
  }
}Context
class Orders(customerRepo: CustomerRepo) {
  def products(order: Order): IO[List[Products]] = {
     val productIds: List[ProductIds] = order.items.flatMap(_.productIds)
     
     productIds.traverse{id : CustomerId => 
     	customerRepo.getById(id)     	
     }
  }
}Г

Context
class Orders(customerRepo: CustomerRepo) {
  def products(order: Order): IO[List[Products]] = {
     val productIds: List[ProductIds] = order.items.flatMap(_.productIds)
     
     productIds.traverse{id : CustomerId => 
     	customerRepo.getById(id)     	
     }
  }
}Г
customerRepo: CustomerRepo,
order: Order,
productIds: List[ProductIds],
id : CustomerId,...
Variable
class Orders(customerRepo: CustomerRepo) {
  def products(order: Order): IO[List[Products]] = {
     val productIds: List[ProductIds] = order.items.flatMap(_.productIds)
     
     productIds.traverse{id : CustomerId => 
     	customerRepo.getById(id)     	
     }
  }
}customerRepo
id
Application
class Orders(customerRepo: CustomerRepo) {
  def products(order: Order): IO[List[Products]] = {
     val productIds: List[ProductIds] = order.items.flatMap(_.productIds)
     
     productIds.traverse{id : CustomerId => 
     	customerRepo.getById(id)     	
     }
  }
}f = customerRepo.getById
v = id
f = order.items.flatMap
v = _.productIds
Lambda
class Orders(customerRepo: CustomerRepo) {
  def products(order: Order): IO[List[Products]] = {
     val productIds: List[ProductIds] = order.items.flatMap(_.productIds)
     
     productIds.traverse{id : CustomerId => 
     	customerRepo.getById(id)     	
     }
  }
}{ id: CustomerId => ... }
_.productIds
let
class Orders(customerRepo: CustomerRepo) {
  def products(order: Order): IO[List[Products]] = {
     val productIds: List[ProductIds] = order.items.flatMap(_.productIds)
     
     productIds.traverse{id : CustomerId => 
     	customerRepo.getById(id)     	
     }
  }
}x = productIds
Free
Free
idFree(e) = {id}
Free
customerRepo.getById(id)Free(e) = {customerRepo, id}
Free
{id : CustomerId => 
     	customerRepo.getById(id) 
}  Free(e) = {customerRepo}
Free
val productIds: List[ProductIds] = 
	order.items.flatMap(_.productIds)
productIds.traverse{id : CustomerId => 
	customerRepo.getById(id)     	
}Free(e) = {order, customerRepo}
Free
def products(order: Order): IO[List[Products]] = {
    val productIds: List[ProductIds] = 
    	order.items.flatMap(_.productIds)
    productIds.traverse{id : CustomerId => 
    	customerRepo.getById(id)     	
    }
}Free(e) = {customerRepo}
Free
class Orders(customerRepo: CustomerRepo) {
  def products(order: Order): IO[List[Products]] = {
     val productIds: List[ProductIds] = 
     	order.items.flatMap(_.productIds)
     
     productIds.traverse{id : CustomerId => 
     	customerRepo.getById(id)     	
     }
  }
}Free(e) = {}
Free
class Orders(customerRepo: CustomerRepo) {
  def products(order: Order): IO[List[Products]] = {
     val productIds: List[ProductIds] = 
     	order.items.flatMap(_.productIds)
     
     productIds.traverse{id : CustomerId => 
     	customerRepo.getById(id)     	
     }
  }
}Free(e) = {}
Top-level terms are closed
Effects
Effects
- Calculation
- Unlimited recursion
- Allocating on stack
- Allocating on heap
- Reading global variables
- Aborting execution with an error
- Operating on locally mutable state
- Operating on shared mutable state
- Waiting
- Concurrent execution
- Communicating between subprocesses
- Reading from file system
- Writing to file system
- Network communication
Effects
- Calculation
- Unlimited recursion
- Allocating on stack
- Allocating on heap
- Reading global variables
- Aborting execution with an error
- Operating on locally mutable state
- Operating on shared mutable state
- Waiting
- Concurrent execution
- Communicating between subprocesses
- Reading from file system
- Writing to file system
- Network communication

Effects
- Calculation
- Unlimited recursion
- Allocating on stack
- Allocating on heap
- Reading global variables
- Aborting execution with an error
- Operating on locally mutable state
- Operating on shared mutable state
- Waiting
- Concurrent execution
- Communicating between subprocesses
- Reading from file system
- Writing to file system
- Network communication
Non-Effects
Effects
Effects
- Calculation
- Unlimited recursion
- Allocating on stack
- Allocating on heap
- Reading global variables
- Aborting execution with an error
- Operating on locally mutable state
- Operating on shared mutable state
- Waiting
- Concurrent execution
- Communicating between subprocesses
- Reading from file system
- Writing to file system
- Network communication
Non-Effects
Effects
Unison
Paul Chiusano
Runar Bjarnason
Chris Penner

Unison
readInt : Text -> Int
readInt text = Optional.getOrElse +0 (Int.fromText text)
readInts: Text -> List Int
readInts text = List.map readInt (Text.split ?, text)Unison
readInt: Text -> Int
readInt = fromText >> getOrElse +0 
readInts: Text -> [Int]
readInts = split ?, >> map readInt Unison
readInt': Text -> {Abort} Int
readInt' = Int.fromText >> toAbort
readInts': Text -> {Abort} [Int]
readInts' = split ?, >> map readInt'
readIntsOrEmpty : Text -> {} [Int]
readIntsOrEmpty txt = toDefaultValue! [] '(readInts' txt)Unison Effects
structural ability Abort where
  stop: forall a. {Abort} a
toAbort : Optional a -> {Abort} a
toAbort opt = match opt with
  None -> stop
  Some a -> a
toDefaultValue! : a -> '{Abort} a -> a
toDefaultValue! value expr = 
   handle force expr with cases
      { Abort.stop -> k } -> value
      { result } -> resultUnison Effects
parseInt: Text -> {Abort} Int
parseInt = toAbort << Int.fromText
> (getLine : Handle -> {IO, Exception} Text)
parseNextLine : Handle ->{IO, Exception, Abort} Int
parseNextLine = getLine >> parseInt
> (open: FilePath -> FileMode -> {IO, Exception} Handle)
parseFileInts : Text ->{IO, Exception, Abort} [Int]
parseFileInts path =  
    file = open (FilePath path) Read
    l1 = parseNextLine file
    l2 = parseNextLine file
    l3 = parseNextLine file
    [l1, l2, l3]
Unison Effects
sqrt : Float -> {Abort} Float
sqrt n = if n < 0.0 then abort else Float.sqrt n
sqrtSum: Float -> Float -> {Abort} Float
sqrtSum x y = sqrt x + sqrt yUnison Effects
sqrt : Float -> {Abort} Float
sqrt n = if n < 0.0 then abort else Float.sqrt n
sqrtSum': Float -> Float -> Float
sqrtSum' x = 
        sqrtX = sqrt x
        y -> sqrtX + sqrt yUnison Effects
sqrt : Float -> {Abort} Float
sqrt n = if n < 0.0 then abort else Float.sqrt n
sqrtSum': Float -> Float -> Float
sqrtSum' x = 
        sqrtX = sqrt x
        y -> sqrtX + sqrt yFloat -> {} Float -> {Abort} FloatFloat -> {Abort} Float -> {} FloatFloat ->{Abort} Float ->{Abort} Float
✔
❌
❌
Unison Effects
> (open: FilePath -> FileMode -> {IO, Exception} Handle)
> (parseInt: Text -> {Abort} Int)
> (getLine : Handle -> {IO, Exception} Text)
parseFileLines path = 
    file = open (FilePath path) Read
    i -> i + parseInt (getLine file)Text -> {} Int -> {IO, Exception, Abort} IntText -> {IO, Exception, Abort} Int -> {} IntText ->{IO, Exception} Int ->{IO, Exception, Abort} Int
✔
❌
❌
Effectful Lambda
Replacing Monads with effects
Optional a 
Either e a
Writer w a\List w
Reader r a
State s a
Try a
IO a
{Abort}
{Throw e}
{Stream w}
{Context r}
{Store s}
{Exception}
{IO}
Effect handlers
unique ability GenerateInt where
    next: {GenerateInt} Int
const : Int -> '{GenerateInt, e} a ->{e} a
const value expr = 
   handle !expr with cases
      { next -> k} -> const value '(k value)
      { res } -> resk : Int -> '{GenerateInt, e} a> const +2 '[next, next, next]
           ⧩
           [+2, +2, +2]Effect handlers
unique ability GenerateInt where
    next: {GenerateInt} Int
from : Int -> '{GenerateInt, e} a -> {e} a
from start expr = 
   handle !expr with cases
      { next -> k} -> from (start + +1) '(k start)
      { res } -> res > from +2 '[next, next, next]
           ⧩
           [+2, +3, +4]Effect handlers
unique ability GenerateInt where
    next: {GenerateInt} Int
fallBack : a -> '{GenerateInt, e} a -> {e} a
fallBack default expr = 
   handle !expr with cases
      { next -> _ } -> default
      { res } -> res> fallBack [+1] '[next, next]
           ⧩
           [+1]
  
 > fallBack [+1] '[]
           ⧩
           []Effect handlers
unique ability GenerateInt where
    next: {GenerateInt} Int
withRandom : '{GenerateInt, e} a -> {Random, e} a
withRandom expr = 
   handle !expr with cases
      { next -> k} -> withRandom '(k !Random.int)
      { res } -> res> splitmix 1000 '(withRandom '[next, next, next])
            ⧩
   [-8103687766989403219, +7503088898446438863, -8052551751343754592]Effect handlers
unique ability GenerateInt where
    next: {GenerateInt} Int
withPrint : '{GenerateInt, e} a -> {IO, Exception, e} a
withPrint expr = 
    handler  = cases
        { next -> k} -> 
                x = !Random.int
                printLine ("generated " ++ toText x)
                res = handle k x with handler
                printLine "done"
                res
        { res } -> res
    Random.run do
        handle !expr with handlerEffect handlers
unique ability GenerateInt where
    next: {GenerateInt} Int
search : List Int -> '{GenerateInt, e} Optional a -> {e} Optional a
search values expr = 
    handle !expr with cases
      { next -> k} -> List.findMap (i -> search values '(k i)) values
      { res } -> respythagorean : '{GenerateInt} Optional (Int, Int, Int)
pythagorean = do
    a = next
    b = next
    c = next
    if (a * a) + (b * b) == (c * c) then Some (a, b, c) else None
> search (Int.range +1 +8) pythagorean
            ⧩
            Some (+3, +4, +5)
Effect handlers
- Abstraction and Inversion of Control
- Error Handling
- Resource and Scope Control
- Branching and Backtracking
- (Async execution)
unique ability GenerateInt where
    next: {GenerateInt} Int
Effect
- Composition is hard
- eDSL
- No partial handling
- Same but with aftertaste
vs
Monads
- Composable
- Special language feature
- Partial handlers
- Covers a lot of language features



State
case class State[S, A](run: S => (S, A))
case class State[S, A](run: Eval[S => Eval[(S, A)]])
trait State[S, +A]{
   def run(s: S): (S, A)
}State Monad
object State{
	def apply[S, A](a: A): State[S, A] = s => (s, a)
}
trait State[S, +A]{
   def run(s: S): (S, A)
	
   def map[B](f: A => B): State[S, B] = { s => 
      val (s1, a) = run(s)
      (s1, f(a))
   }
   
   def flatMap[B](f: A => State[S, B]) = { s =>
   	  val (s1, a) = run(s)
      f(a).run(s1) 
   }
   	
}State Monad
object State{
	def apply[S, A](a: A): State[S, A] = s => (s, a)
    
    def get[S]: State[S, S] = s => (s, s)
    
    def set[S](s: S): State[S, Unit] = _ => (s, ())
    
    def update[S](f: S => S): State[S, Unit] = s => (f(s), ())
}State Comprehension
def addClient(client: Client): State[WowEnterpriseData, Unit] = for {
    data   <- State.get
    clients = data.clients.updated(client.id, client)
    _      <- State.set(data.copy(clients = clients))
} yield ()
def addProduct(product: Product): State[WowEnterpriseData, Unit] = for {
    data    <- State.get
    products = data.products.updated(product.id, product)
    _       <- State.set(data.copy(products = products))
} yield ()
def addUsage(clientId: ClientId, productId: ProductId): State[WowEnterpriseData, Unit] = for {
    data <- State.get
    usage = data.usage + ((clientId, productId))
    _    <- State.set(data.copy(usage = usage))
} yield ()
def suchEnterpiseLogic(client: Client, product: Product): State[WowEnterpriseData, Unit] = for {
    _ <- addClient(client)
    _ <- addProduct(product)
    _ <- addUsage(client.id, product.id)
} yield ()
    
case class WowEnterpriseData(
    clients: Map[ClientId, Cliend],
    products: Map[ProductId, Product],
    usage: Set[(ClientId, ProductId)]
)WARNING: THIS IS NOT REAL ENTERPRISE-LEVEL CODE
State Comprehension
clients = data.clients.updated(client.id, client)
data.copy(clients = clients)
products = data.products.updated(product.id, product)
data.copy(products = products)
usage = data.usage + ((clientId, productId))
data.copy(usage = usage)
case class WowEnterpriseData(
    clients: Map[ClientId, Cliend],
    products: Map[ProductId, Product],
    usage: Set[(ClientId, ProductId)]
)Mutable State
clients = data.clients.updated(client.id, client)
data.clients = clients
products = data.products.updated(product.id, product)
data.products = products
usage = data.usage + ((clientId, productId))
data.usage = usage
class WowEnterpriseData(
    var clients: Map[ClientId, Cliend],
    var products: Map[ProductId, Product],
    var usage: Set[(ClientId, ProductId)]
)Mutable Danger
class MyState private (private var count: Int)
object MyState {
    def count(using state: MyState): Int        = state.count
    def increment()(using state: MyState): Unit = state.count += 1
    def withState[T](actions: MyState ?=> T): T = {
        given MyState(0)
        actions
    }
}Mutable Danger
def reportState(using MyState): String -> String = {
    name => s"$name current value is ${MyState.count}\n"
}
def compare(using MyState): String = {
    val oldReport = reportState
    for (i <- 0 to 9) {
        MyState.increment()
    }
    val newReport = reportState
    oldReport("old") ++ newReport("new")
}
@main def runMyState(): Unit = MyState.withState{
    println(compare)
}Mutable Danger
def reportState(using MyState): String -> String = {
    name => s"$name current value is ${MyState.count}\n"
}
def compare(using MyState): String = {
    val oldReport = reportState
    for (i <- 0 to 9) {
        MyState.increment()
    }
    val newReport = reportState
    oldReport("old") ++ newReport("new")
}
@main def runMyState(): Unit = MyState.withState{
    println(compare)
}
// old current value is 10
// new current value is 10
Mutable Danger = Capture
def reportState(using MyState): String -> String = {
    val count = MyState.count
    name => s"$name current value is $count\n"
}
def compare(using MyState): String = {
    val oldReport = reportState
    for (i <- 0 to 9) {
        MyState.increment()
    }
    val newReport = reportState
    oldReport("old") ++ { newReport("new") }
}
@main def runMyState(): Unit = MyState.withState{
    println(compare)
}
// old current value is 0
// new current value is 10
Unique Mutable State
def doSomething(data: Data): (Result, Data) = 
	val data1 = data.copy(field1 = value1)
    val data2 = data1.copy(field2 = value2)
    (result, data2)Unique Mutable State
def doSomething(data: Data): (Result, Data) = 
	val data1 = data.copy(field1 = value1)
    val data2 = data1.copy(field2 = value2)
    (result, data2)there is only one reference to data
def doSomething(data: Data): (Result, Data) = 
	data.field1 = value1
    data.field2 = value2
    (result, data)- Automatically rewrite updates to mutation if there is only one reference, a.k.a Functional But In Place (Perseus: Koka + Lean4)
 
 
 
- Track uniqueness in type system, allow direct mutability
Unique Mutable State
- Automatically rewrite updates to mutation if there is only one reference, a.k.a Functional But In Place (Perseus: Koka + Lean4)
 
 
 
- Track uniqueness in type system, allow direct mutability (Clean)
(S) -> (S, A)(*unique S) -> AUnique Mutable State
Uniqueness Types
def reportState(using unique MyState): String -> String = {
    name => s"$name current value is ${MyState.count}\n"
}
Uniqueness Types
def reportState(using unique MyState): String -> String = {
    name => s"$name current value is ${MyState.count}\n"
}def reportState(using unique MyState): String -> String = {
    val count = MyState.count
    name => s"$name current value is $count\n"
}Unique Lambda
Unique Lambda
Effectful Lambda
Uniqueness as Effect
class IO
object Console {
    def readLine(unique io: IO): String = ???
    def writeLine(line: String)(unique io: IO): Unit = ???
}
class File
object File {
    def open[A](use: (unique File, unique IO) -> A)(unique io: IO): A = ???
	def getLine(unique file: File): String = ???
    def putLine(string: String)(unique file: File): String = ???
}
Uniqueness
- Usual types
- no automatic continuations
- refer to values by names
- several vars of the same unique type
- requires annotations
- compose unique values in structures
vs
Effect
- Separate kind, special syntax
- continuations in handlers
- only refer to effect by its methods
- refer to effect by type
- effect inference
- define complex handlers
Rust
Borrowed
Mut
Owned
Rust
Shared
Unique
Owned
static
Affine
struct Account {
    name: String,
    funds: u64,
}
fn add_funds(a: Account, add: u64) -> Account {
    Account {
        funds: a.funds + add,
        ..a
    }
}Affine
struct Account {
    name: String,
    funds: u64,
}
fn add_funds(mut a: Account, add: u64) -> Account {
    a.funds += add;
    a
}Affine
struct Account {
    name: String,
    funds: u64,
}
fn add_funds(mut a: Account, add: u64) -> Account {
    a.funds += add;
    a
}   
fn foo() {
    let mut a = Account {
        name: "Oleg".to_string(),
        funds: 1000,
    };
    let a1 = add_funds(a, 5);
    let a2 = add_funds(a, 5);
}use of moved value: `a`
value used here after move
Affine
struct Account {
    name: String,
    funds: u64,
}
fn add_funds(mut a: Account, add: u64) -> Account {
    a.funds += add;
    a
}
fn foo() {
    let mut a = Account {
        name: "Oleg".to_string(),
        funds: 1000,
    };
    let a1 = add_funds(a, 5);
    let a2 = add_funds(a1, 5);
}Affine
Moving
struct Account {
    name: String,
    funds: u64,
}
fn add_funds(mut a: Account, add: u64) -> Account {
    a.funds += add;
    a
} 
fn foo() {
    let mut a = Account {
        name: "Oleg".to_string(),
        funds: 25,
    };
    let a1 = add_funds(a, 5);
    let a2 = add_funds(a1, 5);
}Referencing
struct Account {
    name: String,
    funds: u64,
}
fn add_funds(a: &mut Account, add: u64) {
    a.funds += add;
}
fn foo() {
    let mut a = Account {
        name: "Oleg".to_string(),
        funds: 1000 ,
    };
    add_funds(&mut a, 5);
    add_funds(&mut a, 5);
}
Referencing
struct Account {
    name: String,
    funds: u64,
}
fn add_funds(a: &mut Account, add: u64) {
    a.funds += add;
}
fn foo() {
    let mut a = Account {
        name: "Oleg".to_string(),
        funds: 1000 ,
    };
    let ar1 = &mut a;
    add_funds(ar1, 5);
    
    let ar2 = &mut a;
    add_funds(ar2, 5);
}
Referencing?
struct Account {
    name: String,
    funds: u64,
}
fn add_funds(a: &mut Account, add: u64) {
    a.funds += add;
}
fn foo() {
    let mut a = Account {
        name: "Oleg".to_string(),
        funds: 1000 ,
    };
    let ar1 = &mut a;
    let ar2 = &mut a;
    add_funds(ar1, 5);  
    add_funds(ar2, 5);
}cannot borrow `a` as mutable more than once at a time
Lifetimes
There is only one mutable reference to each portion of data
Uniqueness Types
Lifetimes
struct Account {
    name: String,
    funds: u64,
}
fn add_funds(a: &mut Account, add: u64) {
    a.funds += add;
}
fn foo() {
    let mut a = Account {
        name: "Oleg".to_string(),
        funds: 1000 ,
    };
    let ar1 = &mut a;
    add_funds(ar1, 5);
    
    let ar2 = &mut a;
    add_funds(ar2, 5);
}
Lifetimes
struct Account {
    name: String,
    funds: u64,
}
fn add_funds<'a>(a: &'a mut Account, add: u64) {
    a.funds += add;
}
fn foo() {
    let mut a = Account {
        name: "Oleg".to_string(),
        funds: 1000 ,
    };
    let ar1 = &mut a;
    add_funds(ar1, 5);
    
    let ar2 = &mut a;
    add_funds(ar2, 5);
}
Lifetimes
struct Funds<'a> {
    account: &'a mut Account,
}
impl <'a> Funds<'a> {
    fn add(self, add: u64) {
        self.account.funds += add;
    }
}
fn funds<'a>(account: &'a mut Account) -> Funds<'a> {
    Funds { account }
}
fn foo() {
    let mut a = Account {
        name: "Oleg".to_string(),
        funds: 1000,
    };
    funds(&mut a).add(5);
    
    funds(&mut a).add(5);
}Lifetimes
struct Transfer<'a> {
    from: &'a mut Account,
    to: &'a mut Account,
}
impl <'a> Transfer<'a> {
    fn transfer(self, amount: u64) {
        self.from.funds -= amount;
        self.to.funds += amount;
    }
}
fn transfer<'a>(from: &'a mut Account, to: &'a mut Account) -> Transfer<'a> {
    Transfer { from, to }
}
fn foo() {
    let mut a = Account { name: "Oleg".to_string(), funds: 1000 };
    let mut b = Account { name: "Vasya".to_string(), funds: 1000 };
    transfer(&mut a, &mut b).transfer(30);
    funds(&mut a).add(5);
    funds(&mut a).add(5);
}Precise Capture
struct Transfer<'a> {
    to: &'a mut Account,
    amount: u64,
}
impl<'a> Transfer<'a> {
    fn commit(self) {
        self.to.funds += self.amount;
    }
}
fn transfer<'a, 'b>(from: &'a mut Account, to: &'b mut Account, amount: u64) -> Transfer<'b> {
    from.funds -= amount;
    Transfer { to, amount }
}
fn foo() {
    let mut a = Account { name: "Oleg".to_string(), funds: 1000 };
    let mut b = Account { name: "Vasya".to_string(), funds: 1000 };
    let t = transfer(&mut a, &mut b, 10);
    funds(&mut a).add(5);
    t.commit();
    funds(&mut b).add(5);
}Self reference problem
struct OwnedAndRef {
    data: Vec<i32>,
    element: &mut '(data) i32
}
Rust Mutable Refs
- perform uniqueness check
- can express precise capture set
- too specialized for memory safety
- problems with structural self-referencing
Scala 3 Effects?
- Refer by type via givens\implicits
- Limit capturing to chosen types
- Don't bother about lifetimes on stack
- Express precise capture sets in types
- Be able to express self-capturing
Path-Dependent
trait Collection{
    type Item
    def get(i: Int): Item
}
def head(c: Collection): c.Item = c.get(0)
val second: (c: Collection) => c.Item = _.get(1)Path-Dependent
trait Pair{
    type First
    type Second
    val first: First
    val second: Second
    override def toString = s"($first, $second)"
}
def rotateLeft(p: Pair{type First <: Pair}): Pair{
        type First = p.first.First
        type Second = Pair{
                type First = p.first.Second
                type Second = p.Second
        } 
    } = ???
Capture Set
def foo(x: X^, y: Y): Z^{x} = ???Capture Set
def foo(x: X^, y: Y): Z^{x} = ???Capture Set
def foo(x: X^, y: Y): Z^{x} = ???def foo(x: X, y: Y): A ->{x} B = ???{}           // Nothing
{a, b, c}    // a | b | c
{cap}        // AnyX^{} <: X^{a, b} <: X^{c, a, b} <: X^{cap} Capture Set
 SomeClass^{x}
 
 A ->{x} B{}           // Nothing
{a, b, c}    // a | b | c
{cap}        // AnyX^{} <: X^{a, b} <: X^{c, a, b} <: X^{cap} type A => B = A ->{cap} BNo Reader
@capability trait Get[+A](val value: A)
object Get:
    def get[A](using get: Get[A]^): A = get.valueNo Copying
@capability class State[A](private var value: A)
object State:
    def get[A](using state: State[A]^): A = state.value
    def set[A](value: A)(using state: State[A]^): Unit = state.value = value
    def modify[A](f: A => A)(using state: State[A]^): Unit = 
        state.value = f(state.value)No Copying
@capability class State[A](private var state: A):
    def get: A = state
    def set(value: A): Unit = state = value
    def modify(f: A => A): Unit = state = f(state)
object State:
    def of[A](using state: State[A]): State[A] = stateNo Option
final class Aborted extends RuntimeException("", null, false, false)
type Abort = CanThrow[Aborted]
def getOrElse[T](default: T)(block: => T throws Aborted): T =
	try block
	catch case _: Aborted => default
No Comprehension
def lol(using Abort^, Get[String]^, State[Int]^): String = 
    if State.of.get > 10 then Abort.abort
    State.of.modify(_ + 1)
    s"Name is ${Get.get}"
No Comprehension
val lol: (Abort^, Get[String]^, State[Int]^) ?-> String = 
    if State.of.get > 10 then Abort.abort
    State.of.modify(_ + 1)
    s"Name is ${Get.get}"
Effects?
> (open: FilePath -> FileMode -> {IO, Exception} Handle)
> (parseInt: Text -> {Abort} Int)
> (getLine : Handle -> {IO, Exception} Text)
parseFileLine: Text ->{IO, Exception} Int ->{IO, Exception, Abort} Int
parseFileLines path = 
    file = open (FilePath path) Read
    i -> i + parseInt (getLine file)Effects!
def open(path: Path, mode: AccessMode)(using IO^, IOExceptions^): Handle
def parseInt(string: String)(using Abort): Int
def getLine(handle: Handle)(using IO^, IOExceptions^): String
def parseFileLines(path: Path)(using IO^, IOExceptions^): Int -> (IO^, IOExceptions^, Abort^) ?-> Int = 
  val file = open(path, READ)
  i => i + parseInt(getLine(file))        @capability trait Stream[A]:
    def emit(a: A): UnitGenerators
@capability trait Stream[A]:
    def emit(a: A): Unit
    
def range(from: Int, until: Int)(using stream: Stream[Int]): Unit = 
    if from < until then 
        stream.emit(from)
        range(from + 1, until)
Generators
@capability trait Stream[A]:
    def emit(a: A): Unit
    
def toList[A](stream: Stream[A]^ ?-> Unit): List[A] = 
    val builder = List.newBuilder[A]
    stream(using new Stream[A]:
        def emit(a: A) = builder += a)
    builder.result()Generators
@capability trait Stream[A]:
    def emit(a: A): Unit
    
def toList[A](stream: Stream[A]^ ?-> Unit): List[A] = 
    val builder = List.newBuilder[A]
    given Stream[A] with
        def emit(a: A) = builder += a
    stream
    builder.result()Generators
@capability trait Stream[A]:
    def emit(a: A): Unit
    
    
def map[A, B](es: Stream[A]^ ?-> Unit)(f: A -> B)(using bs: Stream[B]^): Unit = 
    es(using new Stream[A]:
        def emit(a: A) = bs.emit(f(a)))
Generators
@capability trait Stream[A]:
    def emit(a: A): Unit
    
    
def map[A, B](es: Stream[A]^ ?-> Unit)(f: A -> B)(using bs: Stream[B]^): Unit = 
    es(using new Stream[A]:
        def emit(a: A) = bs.emit(f(a)))
def flatMap[A, B](es: Stream[A]^ ?-> Unit)(f: A -> Stream[B]^ ?-> Unit)(using bs: Stream[B]^): Unit =
    es(using new Stream[A]:
        def emit(a: A) = f(a)(using new Stream[B]:
            def emit(b: B) = bs.emit(b)))Generators
@capability trait Stream[A]:
    def emit(a: A): Unit
    
    
def map[A, B](es: Stream[A]^ ?-> Unit)(f: A -> B)(using bs: Stream[B]^): Unit = 
    es(using new Stream[A]:
        def emit(a: A) = bs.emit(f(a)))
def flatMap[A, B](es: Stream[A]^ ?-> Unit)(f: A -> Stream[B]^ ?-> Unit)(using bs: Stream[B]^): Unit =
    es(using new Stream[A]:
        def emit(a: A) = f(a)(using new Stream[B]:
            def emit(b: B) = bs.emit(b)))
    
def filter[A](es: Stream[A]^ ?-> Unit)(p: A -> Boolean)(using bs: Stream[A]^): Unit = 
    es(using new Stream[A]:
        def emit(a: A) = if p(a) then bs.emit(a))Generators
Stream.toList{
    Stream.range(1, 6)
    Stream.range(10, 16)
    Stream.range(20, 24)
    } // List(1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15, 20, 21, 22, 23)
    
Stream.toList{
    for(i <- 1 to 5) 
    	Stream.emit(i * i)
    } // List(1, 4, 9, 16, 25)Generators
Capabilities
- Reusing all the syntactical benefits of implicit (and problems)
	- Mark effects via usingor?->
- No inference, only manual laborannotations
- Easy define, compose handlers as 1st class values
- Use contextual lambda blocks
 
- Mark effects via 
- Embrace old-school imperative programming that's could be much more safe now
	- Replace forcomprehensions with imperative sequencing
- Explore Loom's virtual threads for massive concurrency
- Use mutability in capability handlers
- Use CanThrow for exceptions
 
- Replace 
Thank^{you}
Telegram: t.me/odomontois
Discord : https://discord.gg/jrunbnBuNs


Copy of Effects & Capture
By Oleg Nizhnik
Copy of Effects & Capture
- 359
 
   
   
  