diff --git a/README.md b/README.md index 75e65a0..1c3ba0e 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # Swift Sessions -**Swift Sessions** is a comprehensive Swift package that implements binary [session types](https://en.wikipedia.org/wiki/Session_type), providing a robust framework for ensuring safe and structured communication in concurrent systems. +**Swift Sessions** is a Swift package that implements binary [session types](https://en.wikipedia.org/wiki/Session_type), providing a robust framework for ensuring safe and structured communication in concurrent systems. The library currently supports the following features: - session type inference (only with closures) - session types with binary branches - dynamic linearity checking - duality constraints on session types -- client/server architecture for session initialization +- session initialization with client/server architecture ## Authors and Acknowledgments @@ -17,21 +17,21 @@ This library was developed as part of a Bachelor’s degree thesis project at th ### Programming Styles This library offers two distinct styles for managing session types: -- **Continuation with Closures**: protocol continuations are passed as closures. This approach makes the flow of logic explicit and easy to follow within the closure context. It's particularly useful for straightforward communication sequences. +- **Continuation with closures**: protocol continuations are passed as closures. This approach makes the flow of logic explicit and easy to follow within the closure context. It's particularly useful for straightforward communication sequences. ```swift - await Session.create { c in + await Session.create { e in // One side of the communication channel - await Session.recv(from: c) { num, c in - await Session.send(num % 2 == 0, on: c) { c in - await Session.close(c) + await Session.recv(from: e) { num, e in + await Session.send(num % 2 == 0, on: e) { e in + await Session.close(e) } } } _: { c in // Another side of the communication channel - await Session.send(42, on: c) { c in - await Session.recv(from: c) { isEven, c in - await Session.close(c) + await Session.send(42, on: e) { e in + await Session.recv(from: e) { isEven, e in + await Session.close(e) } } } @@ -40,22 +40,22 @@ This library offers two distinct styles for managing session types: - Pros: Complete type inference of the communication protocol. - Cons: Nested code structure. -- **Channel Passing for Continuation**: this style involves returning continuation channels from communication primitives. It offers greater flexibility, enabling more modular and reusable code, particularly for complex communication sequences. +- **Continuations with endpoint passing**: this style involves returning continuation endpoints from communication primitives. It offers greater flexibility, enabling more modular and reusable code, particularly for complex communication sequences. ```swift - typealias Communication = Channel), Empty>)> - + typealias Protocol = Endpoint), Empty>)> + // One side of the communication channel - let c = await Session.create { (c: Communication) in - let (num, c1) = await Session.recv(from: c) - let c2 = await Session.send(num % 2 == 0, on: c1) - await Session.close(c2) + let e = await Session.create { (e: Protocol) in + let (num, e1) = await Session.recv(from: e) + let e2 = await Session.send(num % 2 == 0, on: e1) + await Session.close(e2) } - + // Another side of the communication channel - let c1 = await Session.send(42, on: c) - let (isEven, c2) = await Session.recv(from: c1) - await Session.close(c2) + let e1 = await Session.send(42, on: e) + let (isEven, e2) = await Session.recv(from: e1) + await Session.close(e2) ``` - Pros: Simplicity, particularly for avoiding deep indentation. @@ -64,7 +64,8 @@ This library offers two distinct styles for managing session types: Each style provides a unique approach to handling session-based binary communication and comes with its own pros and cons. By supporting both styles, SwiftSessions allows you to choose the best approach (or use both in a hybrid way!) according to your needs and coding preferences. For additional examples, see the [Tests](Tests) folder. - + + ### Client/Server Architecture Instead of creating disposable sessions as seen in the previous examples, you can also initialize sessions using a client/server architectural style. @@ -73,19 +74,19 @@ A **server** is responsible for creating and managing multiple sessions that can ```swift // Server side -let server = await Server { c in - await Session.recv(from: c) { num, c in - await Session.send(num % 2 == 0, on: c) { c in - await Session.close(c) +let server = await Server { e in + await Session.recv(from: e) { num, e in + await Session.send(num % 2 == 0, on: e) { e in + await Session.close(e) } } } // Client side -let c1 = await Client(for: server) { c in - await Session.send(42, on: c) { c in - await Session.recv(from: c) { isEven, c in - await Session.close(c) +let c1 = await Client(for: server) { e in + await Session.send(42, on: e) { e in + await Session.recv(from: e) { isEven, e in + await Session.close(e) } } } diff --git a/Sources/SwiftSessions/Client.swift b/Sources/SwiftSessions/Client.swift index cee1213..a1e1b73 100644 --- a/Sources/SwiftSessions/Client.swift +++ b/Sources/SwiftSessions/Client.swift @@ -15,10 +15,10 @@ public class Client { /// - Parameters: /// - server: The server instance to connect to. /// - closure: The closure to execute on the client's channel after connecting. - init(for server: Server, _ closure: @escaping (_: Channel) async -> Void) async { + init(for server: Server, _ closure: @escaping (_: Endpoint) async -> Void) async { let channel: AsyncChannel = AsyncChannel() await server.connect(with: channel) - let c = Channel(with: channel) + let c = Endpoint(with: channel) Task { await closure(c) } diff --git a/Sources/SwiftSessions/Empty.swift b/Sources/SwiftSessions/Empty.swift index 9bceb80..9300b0f 100644 --- a/Sources/SwiftSessions/Empty.swift +++ b/Sources/SwiftSessions/Empty.swift @@ -7,7 +7,7 @@ import Foundation -/// Represents an empty type used for parts of a channel where no message sending or receiving is allowed. +/// Represents an empty type used for parts of an endpoint where no message sending or receiving is allowed. enum Empty { } diff --git a/Sources/SwiftSessions/Channel.swift b/Sources/SwiftSessions/Endpoint.swift similarity index 79% rename from Sources/SwiftSessions/Channel.swift rename to Sources/SwiftSessions/Endpoint.swift index 6a57ce7..594f9cc 100644 --- a/Sources/SwiftSessions/Channel.swift +++ b/Sources/SwiftSessions/Endpoint.swift @@ -8,25 +8,25 @@ import Foundation import AsyncAlgorithms -/// Represents a communication channel that enforces session types. +/// Represents a communication endpoint that enforces session types. /// /// This class provides a safe and linear way to communicate between different parts of your program using session types. -/// It guarantees that the channel is consumed only once, enforcing the expected communication pattern. +/// It guarantees that the endpoint is consumed only once, enforcing the expected communication pattern. /// /// - Parameters: -/// - A: The type of messages that can be sent on the channel. -/// - B: The type of messages that can be received on the channel. -public final class Channel { +/// - A: The type of messages that can be sent to the endpoint. +/// - B: The type of messages that can be received from the endpoint. +public final class Endpoint { /// Underlying asynchronous channel for communication. public let asyncChannel: AsyncChannel /// A read-only flag indicating whether the instance has been consumed. /// - /// This property is set to `true` when the channel is consumed and cannot be consumed again. + /// This property is set to `true` when the endpoint is consumed and cannot be consumed again. private(set) var isConsumed: Bool = false - /// Initializes a new channel with the given asynchronous channel. + /// Initializes a new endpoint with the given asynchronous channel. /// - Parameter channel: The underlying asynchronous channel for communication. init(with channel: AsyncChannel) { self.asyncChannel = channel @@ -34,8 +34,8 @@ public final class Channel { /// Initializes a new channel from an existing channel /// - Parameter channel: The channel from which to create the new channel. - init(from channel: Channel) { - self.asyncChannel = channel.asyncChannel + init(from endpoint: Endpoint) { + self.asyncChannel = endpoint.asyncChannel } /// Deinitializes the channel and ensures it has been consumed. @@ -59,7 +59,7 @@ public final class Channel { /// - Throws: a fatal error if the channel has already been consumed. private func consume() { guard !isConsumed else { - fatalError("\(self.description) was used twice") + fatalError("\(self.description) was consumed twice") } isConsumed = true } @@ -71,7 +71,7 @@ public final class Channel { } -extension Channel where A == Empty { +extension Endpoint where A == Empty { /// Receives an element from the async channel /// @@ -86,7 +86,7 @@ extension Channel where A == Empty { } -extension Channel where B == Empty { +extension Endpoint where B == Empty { /// Sends the given element on the async channel /// @@ -101,7 +101,7 @@ extension Channel where B == Empty { } -extension Channel where A == Empty, B == Empty { +extension Endpoint where A == Empty, B == Empty { /// Resumes all the operations on the underlying asynchronous channel /// and terminates the communication diff --git a/Sources/SwiftSessions/Server.swift b/Sources/SwiftSessions/Server.swift index 373e642..ca554e7 100644 --- a/Sources/SwiftSessions/Server.swift +++ b/Sources/SwiftSessions/Server.swift @@ -16,15 +16,15 @@ public class Server { /// Initializes a new server instance that listens for client sessions. /// - Parameter closure: The closure to execute on the server's channel for each session. - init(_ closure: @escaping (_: Channel) async -> Void) async { + init(_ closure: @escaping (_: Endpoint) async -> Void) async { publicChannel = AsyncChannel() Task { while true { for await request in publicChannel { let asyncChannel = request as! AsyncChannel - let channel = Channel(with: asyncChannel) + let endpoint = Endpoint(with: asyncChannel) Task { - await closure(channel) + await closure(endpoint) } } } diff --git a/Sources/SwiftSessions/Session/Session+Closures.swift b/Sources/SwiftSessions/Session/Session+Closures.swift index f2b2162..f003b58 100644 --- a/Sources/SwiftSessions/Session/Session+Closures.swift +++ b/Sources/SwiftSessions/Session/Session+Closures.swift @@ -15,55 +15,55 @@ extension Session { /// Sends a message on the channel and invokes the specified closure upon completion. /// - Parameters: - /// - payload: The payload to be sent on the channel. - /// - chan: The channel on which the payload is sent. + /// - payload: The payload to be sent to the endpoint. + /// - endpoint: The endpoint to which the payload is sent. /// - continuation: A closure to be invoked after the send operation completes. - /// This closure receives the continuation channel for further communication. - static func send(_ payload: A, on channel: Channel<(A, Channel), Empty>, continuation: @escaping (Channel) async -> Void) async { - await channel.send(payload) - await continuation(Channel(from: channel)) + /// This closure receives the continuation endpoint for further communication. + static func send(_ payload: A, on endpoint: Endpoint<(A, Endpoint), Empty>, continuation: @escaping (Endpoint) async -> Void) async { + await endpoint.send(payload) + await continuation(Endpoint(from: endpoint)) } /// Receives a message from the channel and invokes the specified closure upon completion. /// - Parameters: - /// - chan: The channel from which the message is received. + /// - endpoint: The endpoint from which the message is received. /// - continuation: A closure to be invoked after the receive operation completes. - /// This closure receives the received message and the continuation channel. - static func recv(from channel: Channel)>, continuation: @escaping ((A, Channel)) async -> Void) async { - let msg = await channel.recv() - await continuation((msg as! A, Channel(from: channel))) + /// This closure receives the received message and the continuation endpoint. + static func recv(from endpoint: Endpoint)>, continuation: @escaping ((A, Endpoint)) async -> Void) async { + let msg = await endpoint.recv() + await continuation((msg as! A, Endpoint(from: endpoint))) } /// Offers a choice between two branches on the given channel, and executes the corresponding closure based on the selected branch. /// - Parameters: - /// - channel: The channel on which the choice is offered. This channel expects a value indicating the selected branch (`true` for the first branch, `false` for the second branch). - /// - side1: The closure to be executed if the first branch is selected. This closure receives a channel of type `Channel`. - /// - side2: The closure to be executed if the second branch is selected. This closure receives a channel of type `Channel`. - static func offer(on channel: Channel, Channel>>, _ side1: @escaping (Channel) async -> Void, or side2: @escaping (Channel) async -> Void) async { - let bool = await channel.recv() as! Bool + /// - endpoint: The endpoint on which the choice is offered. This endpoint expects a value indicating the selected branch (`true` for the first branch, `false` for the second branch). + /// - side1: The closure to be executed if the first branch is selected. This closure receives a endpoint of type `Endpoint`. + /// - side2: The closure to be executed if the second branch is selected. This closure receives a endpoint of type `Endpoint`. + static func offer(on endpoint: Endpoint, Endpoint>>, _ side1: @escaping (Endpoint) async -> Void, or side2: @escaping (Endpoint) async -> Void) async { + let bool = await endpoint.recv() as! Bool if bool { - await side1(Channel(from: channel)) + await side1(Endpoint(from: endpoint)) } else { - await side2(Channel(from: channel)) + await side2(Endpoint(from: endpoint)) } } - /// Selects the left branch on the given channel and executes the provided continuation closure. + /// Selects the left branch on the given endpoint and executes the provided continuation closure. /// - Parameters: - /// - channel: The channel on which the left branch is selected. This channel sends a value indicating the left branch selection (`true`). - /// - continuation: A closure to be executed after the left branch is selected. This closure receives a channel of type `Channel`. - static func left(_ channel: Channel, Channel>, Empty>, continuation: @escaping (Channel) async -> Void) async { - await channel.send(true) - await continuation(Channel(from: channel)) + /// - endpoint: The channel on which the left branch is selected. + /// - continuation: A closure to be executed after the left branch is selected. This closure receives a endpoint of type `Endpoint`. + static func left(_ endpoint: Endpoint, Endpoint>, Empty>, continuation: @escaping (Endpoint) async -> Void) async { + await endpoint.send(true) + await continuation(Endpoint(from: endpoint)) } /// Selects the right branch on the given channel and executes the provided continuation closure. /// - Parameters: - /// - channel: The channel on which the right branch is selected. This channel sends a value indicating the right branch selection (`false`). - /// - continuation: A closure to be executed after the right branch is selected. This closure receives a channel of type `Channel`. - static func right(_ channel: Channel, Channel>, Empty>, continuation: @escaping (Channel) async -> Void) async { - await channel.send(false) - await continuation(Channel(from: channel)) + /// - endpoint: The endpoint on which the right branch is selected. + /// - continuation: A closure to be executed after the right branch is selected. This closure receives a endpoint of type `Endpoint`. + static func right(_ endpoint: Endpoint, Endpoint>, Empty>, continuation: @escaping (Endpoint) async -> Void) async { + await endpoint.send(false) + await continuation(Endpoint(from: endpoint)) } } diff --git a/Sources/SwiftSessions/Session/Session+Passing.swift b/Sources/SwiftSessions/Session/Session+Passing.swift index 9557874..17d655c 100644 --- a/Sources/SwiftSessions/Session/Session+Passing.swift +++ b/Sources/SwiftSessions/Session/Session+Passing.swift @@ -7,54 +7,54 @@ import Foundation -/// Extension for the Session class that provides methods using channel passing for continuation for session type communications. +/// Extension for the Session class that provides methods using endpoint passing for continuation for session type communications. /// /// This version of the library includes methods that allow users to send and receive messages, -/// as well as offer and select between branches using channel passing for continuation. +/// as well as offer and select between branches using endpoint passing for continuation. extension Session { - /// Sends a message on the channel and returns the continuation channel + /// Sends a message to the endpoint and returns the continuation endpoint /// - Parameters: - /// - payload: The payload to be sent on the channel. - /// - chan: The channel on which the payload is sent. - /// - Returns: The continuation channel - static func send(_ payload: C, on channel: Channel<(C, Channel), Empty>) async -> Channel { - await channel.send(payload) - return Channel(from: channel) + /// - payload: The payload to be sent to the endpoint. + /// - endpoint: The endpoint to which the payload is sent. + /// - Returns: The continuation endpoint + static func send(_ payload: C, on endpoint: Endpoint<(C, Endpoint), Empty>) async -> Endpoint { + await endpoint.send(payload) + return Endpoint(from: endpoint) } - /// Receives a message from the channel and returns it along with the continuation channel. - /// - Parameter chan: The channel from which the message is received. - /// - Returns: A tuple containing the received message and the continuation channel. - static func recv(from channel: Channel)>) async -> (C, Channel) { - let msg = await channel.recv() - return (msg as! C, Channel(from: channel)) + /// Receives a message from the endpoint and returns it along with the continuation endpoint. + /// - Parameter endpoint: The endpoint from which the message is received. + /// - Returns: A tuple containing the received message and the continuation endpoint. + static func recv(from endpoint: Endpoint)>) async -> (C, Endpoint) { + let msg = await endpoint.recv() + return (msg as! C, Endpoint(from: endpoint)) } - /// Offers a choice between two branches on the given channel, and returns the selected branch. - /// - Parameter channel: The channel on which the choice is offered. This channel expects a value indicating the selected branch (`true` for the first branch, `false` for the second branch). - /// - Returns: An `Or` enum value containing either the first branch channel of type `Channel` or the second branch channel of type `Channel`. - static func offer(_ channel: Channel, Channel>>) async -> Or, Channel> { - let bool = await channel.recv() as! Bool + /// Offers a choice between two branches on the given endpoint, and returns the selected branch. + /// - Parameter endpoint: The endpoint to which the choice is offered. This endpoint expects a value indicating the selected branch (`true` for the first branch, `false` for the second branch). + /// - Returns: An `Or` enum value containing either the first branch endpoint of type `Endpoint` or the second branch endpoint of type `Endpoint`. + static func offer(_ endpoint: Endpoint, Endpoint>>) async -> Or, Endpoint> { + let bool = await endpoint.recv() as! Bool if bool { - return Or.left(Channel(from: channel)) + return Or.left(Endpoint(from: endpoint)) } else { - return Or.right(Channel(from: channel)) + return Or.right(Endpoint(from: endpoint)) } } - /// Selects the left branch on the given channel and returns the continuation channel. - /// - Parameter channel: The channel on which the left branch is selected. This channel sends a value indicating the left branch selection (`true`). - /// - Returns: The continuation channel of type `Channel`. - static func left(_ channel: Channel, Channel>, Empty>) -> Channel { - return Channel(from: channel) + /// Selects the left branch on the given endpoint and returns the continuation endpoint. + /// - Parameter endpoint: The endpoint on which the left branch is selected. + /// - Returns: The continuation endpoint of type `Endpoint`. + static func left(_ endpoint: Endpoint, Endpoint>, Empty>) -> Endpoint { + return Endpoint(from: endpoint) } - /// Selects the right branch on the given channel and returns the continuation channel. - /// - Parameter channel: The channel on which the right branch is selected. This channel sends a value indicating the right branch selection (`false`). - /// - Returns: The continuation channel of type `Channel`. - static func right(_ channel: Channel, Channel>, Empty>) -> Channel { - return Channel(from: channel) + /// Selects the right branch on the given endpoint and returns the continuation endpoint. + /// - Parameter endpoint: The endpoint on which the right branch is selected. + /// - Returns: The continuation endpoint of type `Endpoint`. + static func right(_ endpoint: Endpoint, Endpoint>, Empty>) -> Endpoint { + return Endpoint(from: endpoint) } } diff --git a/Sources/SwiftSessions/Session/Session.swift b/Sources/SwiftSessions/Session/Session.swift index 7dc5302..3f51c89 100644 --- a/Sources/SwiftSessions/Session/Session.swift +++ b/Sources/SwiftSessions/Session/Session.swift @@ -11,57 +11,57 @@ import AsyncAlgorithms /// A utility class for implementing session-based communications using channels class Session { - /// Creates a new session with two dual channels and executes the provided closure on the secondary channel - /// - Parameter closure: The closure to be executed on the secondary channel of type `Chan` - /// - Returns: The primary channel of type `Chan` - static func create(_ closure: @escaping (_: Channel) async -> Void) async -> Channel { + /// Creates a new session with two dual endpoints and executes the provided closure on the secondary endpoint + /// - Parameter closure: The closure to be executed on the secondary endpoint of type `Endpoint` + /// - Returns: The primary endpoint of type `Endpoint` + static func create(_ closure: @escaping (_: Endpoint) async -> Void) async -> Endpoint { let channel: AsyncChannel = AsyncChannel() - let c1 = Channel(with: channel) - let c2 = Channel(with: channel) + let e1 = Endpoint(with: channel) + let e2 = Endpoint(with: channel) Task { - await closure(c2) + await closure(e2) } - return c1 + return e1 } - /// Creates a new session with two dual channels and returns them as a tuple + /// Creates a new session with two dual endpoints and returns them as a tuple. /// - /// This method creates a pair of dual channels of types `Channel` and `Channel`. - /// These channels are linked such that any message sent on one can be received on the other. + /// This method creates a pair of dual endpoints of types `Endpoint` and `Endpoint`. + /// These endpoint are linked such that any message sent on one can be received on the other. /// - /// - Returns: A tuple containing two channels: the first of type `Channel` and the second of type `Channel`. - static func create() -> (Channel, Channel) { + /// - Returns: A tuple containing two endpoints: the first of type `Endpoint` and the second of type `Endpoint`. + static func create() -> (Endpoint, Endpoint) { let channel: AsyncChannel = AsyncChannel() - let c1 = Channel(with: channel) - let c2 = Channel(with: channel) - return (c1, c2) + let e1 = Endpoint(with: channel) + let e2 = Endpoint(with: channel) + return (e1, e2) } - /// Creates a new session with two dual channels and executes the provided closures on each channel + /// Creates a new session with two dual endpoints and executes the provided closures on each endpoint /// - /// This method initializes a pair of dual channels and concurrently executes the provided closures. - /// The first closure operates on the secondary channel of type `Channel`, while the second closure - /// operates on the primary channel of type `Channel`. + /// This method initializes a pair of dual endpoints and concurrently executes the provided closures. + /// The first closure operates on the secondary endpoint of type `Endpoint`, while the second closure + /// operates on the primary endpoint of type `Endpoint`. /// /// - Parameters: - /// - sideOne: The closure to be executed on the secondary channel of type `Channel`. - /// - sideTwo: The closure to be executed on the primary channel of type `Channel`. - static func create(_ sideOne: @escaping (_: Channel) async -> Void, _ sideTwo: @escaping (_: Channel) async -> Void) async { + /// - sideOne: The closure to be executed on the secondary endpoint of type `Channel`. + /// - sideTwo: The closure to be executed on the primary endpoint of type `Endpoint`. + static func create(_ sideOne: @escaping (_: Endpoint) async -> Void, _ sideTwo: @escaping (_: Endpoint) async -> Void) async { let channel: AsyncChannel = AsyncChannel() - let channel1 = Channel(with: channel) - let channel2 = Channel(with: channel) + let endpoint1 = Endpoint(with: channel) + let endpoint2 = Endpoint(with: channel) Task { - await sideOne(channel2) + await sideOne(endpoint2) } Task { - await sideTwo(channel1) + await sideTwo(endpoint1) } } - /// Closes the channel, indicating the end of communication. - /// - Parameter channel: The channel to be closed. - static func close(_ channel: Channel) async { - channel.close() + /// Closes the endpoint, indicating the end of communication. + /// - Parameter endpoint: The endpoint to close the communication. + static func close(_ endpoint: Endpoint) async { + endpoint.close() } } diff --git a/Tests/BranchingTests.swift b/Tests/BranchingTests.swift index 3af8e72..a583655 100644 --- a/Tests/BranchingTests.swift +++ b/Tests/BranchingTests.swift @@ -14,24 +14,24 @@ final class BranchingTests: XCTestCase { func testSumWithBranching() async { // One side of the communication channel - let c = await Session.create { c in - await Session.offer(on: c) { c in - await Session.recv(from: c) { num1, c in - await Session.recv(from: c) { num2, c in + let e = await Session.create { e in + await Session.offer(on: e) { e in + await Session.recv(from: e) { num1, e in + await Session.recv(from: e) { num2, e in let sum: Int = num1 + num2 - await Session.send(sum, on: c) { c in - await Session.close(c) + await Session.send(sum, on: e) { e in + await Session.close(e) } } } - } or: { c in - await Session.recv(from: c) { num, c in + } or: { e in + await Session.recv(from: e) { num, e in var result = 1 for i in 1...num { result *= i } - await Session.send(result, on: c) { c in - await Session.close(c) + await Session.send(result, on: e) { e in + await Session.close(e) } } } @@ -39,11 +39,11 @@ final class BranchingTests: XCTestCase { } // Another side of the communication channel - await Session.left(c) { c in - await Session.send(2, on: c) { c in - await Session.send(3, on: c) { c in - await Session.recv(from: c) { result, c in - await Session.close(c) + await Session.left(e) { e in + await Session.send(2, on: e) { e in + await Session.send(3, on: e) { e in + await Session.recv(from: e) { result, e in + await Session.close(e) assert(result == 5) } } @@ -53,24 +53,24 @@ final class BranchingTests: XCTestCase { func testFactorialWithBranching() async { // One side of the communication channel - let c = await Session.create { c in - await Session.offer(on: c) { c in - await Session.recv(from: c) { num1, c in - await Session.recv(from: c) { num2, c in + let e = await Session.create { e in + await Session.offer(on: e) { e in + await Session.recv(from: e) { num1, e in + await Session.recv(from: e) { num2, e in let sum: Int = num1 + num2 - await Session.send(sum, on: c) { c in - await Session.close(c) + await Session.send(sum, on: e) { e in + await Session.close(e) } } } - } or: { c in - await Session.recv(from: c) { num, c in + } or: { e in + await Session.recv(from: e) { num, e in var result = 1 for i in 1...num { result *= i } - await Session.send(result, on: c) { c in - await Session.close(c) + await Session.send(result, on: e) { e in + await Session.close(e) } } } @@ -78,10 +78,10 @@ final class BranchingTests: XCTestCase { } // Another side of the communication channel - await Session.right(c) { c in - await Session.send(3, on: c) { c in - await Session.recv(from: c) { result, c in - await Session.close(c) + await Session.right(e) { e in + await Session.send(3, on: e) { e in + await Session.recv(from: e) { result, e in + await Session.close(e) assert(result == 6) } } diff --git a/Tests/LinearityTests.swift b/Tests/LinearityTests.swift index 385040f..94d1fee 100644 --- a/Tests/LinearityTests.swift +++ b/Tests/LinearityTests.swift @@ -1,5 +1,5 @@ // -// LinearityChecks.swift +// LinearityTests.swift // // // Created by Alessio Rubicini on 30/05/24. @@ -14,45 +14,45 @@ import XCTest final class LinearityTests: XCTestCase { /// This test aims to verify the library's behavior in situations of linearity violation. - /// In this particular case, the violation is represented by the reuse of a channel. + /// In this particular case, the violation is represented by the reuse of an endpoint. /// - /// Should throw a fatal error saying `Channel<(Bool, Channel), Empty> was consumed twice.` + /// Should throw a fatal error saying `Endpoint<(Bool, Endpoint), Empty> was consumed twice.` func testLinearityViolation1() async { - await Session.create { c in - await Session.recv(from: c) { num, c1 in - await Session.send(num % 2 == 0, on: c1) { c2 in - await Session.close(c2) + await Session.create { e in + await Session.recv(from: e) { num, e1 in + await Session.send(num % 2 == 0, on: e1) { e2 in + await Session.close(e2) - // Using channel c1 again + // Using endpoint e1 again // This is a linearity violation - await Session.send(false, on: c1) { c3 in - await Session.close(c3) + await Session.send(false, on: e1) { e3 in + await Session.close(e3) } } } - } _: { c in - await Session.send(42, on: c) { c1 in - await Session.recv(from: c1) { (isEven: Bool, c2) in - await Session.close(c2) + } _: { e in + await Session.send(42, on: e) { e1 in + await Session.recv(from: e1) { (isEven: Bool, e2) in + await Session.close(e2) } } } } /// This test aims to verify the library's behavior in situations of linearity violation. - /// In this particular case, the violation is represented by the missing use of a channel. + /// In this particular case, the violation is represented by the missing use of an endpoint. /// - /// Should throw a fatal error saying `Channel Channel)> was not consumed.` + /// Should throw a fatal error saying `Endpoint Endpoint)> was not consumed.` func testLinearityViolation2() async { - await Session.create { c in - await Session.recv(from: c) { num, c1 in - await Session.send(num % 2 == 0, on: c1) { c2 in - await Session.close(c2) + await Session.create { e in + await Session.recv(from: e) { num, e in + await Session.send(num % 2 == 0, on: e) { e in + await Session.close(e) } } - } _: { c in - await Session.send(42, on: c) { c1 in - // Not using channel c + } _: { e in + await Session.send(42, on: e) { e1 in + // Not using endpoint e1 // This is a linearity violation } } diff --git a/Tests/LoopTests.swift b/Tests/LoopTests.swift index 3b7efa4..bc1c1ff 100644 --- a/Tests/LoopTests.swift +++ b/Tests/LoopTests.swift @@ -16,17 +16,17 @@ final class LoopTests: XCTestCase { var sum = 0 let numbers = [1, 5, 22, 42, 90] - let s = await Server { c in - await Session.recv(from: c) { num, c in + let s = await Server { e in + await Session.recv(from: e) { num, e in sum += num - await Session.close(c) + await Session.close(e) } } for number in numbers { - let _ = await Client(for: s) { c in - await Session.send(number, on: c) { c in - await Session.close(c) + let _ = await Client(for: s) { e in + await Session.send(number, on: e) { e in + await Session.close(e) } } } diff --git a/Tests/MathTests.swift b/Tests/MathTests.swift index 270d5c8..1f5df80 100644 --- a/Tests/MathTests.swift +++ b/Tests/MathTests.swift @@ -18,67 +18,66 @@ final class MathTests: XCTestCase { // A server that provides two groups of mathematical operations: // - basic arithmetic operations // - logarithms operations - let s = await Server { c in - await Session.offer(on: c) { c in + let s = await Server { e in + await Session.offer(on: e) { e in // Basic arithmetic operations - await Session.offer(on: c) { c in + await Session.offer(on: e) { e in // Addition - await Session.recv(from: c) { num1, c in - await Session.recv(from: c) { num2, c in + await Session.recv(from: e) { num1, e in + await Session.recv(from: e) { num2, e in let result: Int = num1 + num2 - await Session.send(result, on: c) { c in - await Session.close(c) + await Session.send(result, on: e) { e in + await Session.close(e) } } } - } or: { c in + } or: { e in // Substraction - await Session.recv(from: c) { num1, c in - await Session.recv(from: c) { num2, c in + await Session.recv(from: e) { num1, e in + await Session.recv(from: e) { num2, e in let result: Int = num1 - num2 - await Session.send(result, on: c) { c in - await Session.close(c) + await Session.send(result, on: e) { e in + await Session.close(e) } } } } - } or: { c in + } or: { e in // Logarithms operations - await Session.offer(on: c) { c in + await Session.offer(on: e) { e in // Natural logarithm - await Session.recv(from: c) { (number: Double, c) in + await Session.recv(from: e) { (number: Double, e) in let commonLogarithm = log(number) - await Session.send(commonLogarithm, on: c) { c in - await Session.close(c) + await Session.send(commonLogarithm, on: e) { e in + await Session.close(e) } } - } or: { c in + } or: { e in // Common logarithm - await Session.recv(from: c) { (number: Double, c) in + await Session.recv(from: e) { (number: Double, e) in let commonLogarithm = log10(number) - await Session.send(commonLogarithm, on: c) { c in - await Session.close(c) + await Session.send(commonLogarithm, on: e) { e in + await Session.close(e) } } } } - } // A client that uses the addition operation provided by the server - let _ = await Client(for: s) { c in + let _ = await Client(for: s) { e in // Choose Basic arithmetic operations - await Session.left(c) { c in + await Session.left(e) { e in // Choose addition operation - await Session.left(c) { c in - await Session.send(5, on: c) { c in - await Session.send(5, on: c) { c in - await Session.recv(from: c) { result, c in - await Session.close(c) + await Session.left(e) { e in + await Session.send(5, on: e) { e in + await Session.send(5, on: e) { e in + await Session.recv(from: e) { result, e in + await Session.close(e) assert(result == 10) } } @@ -88,15 +87,15 @@ final class MathTests: XCTestCase { } // A client that uses the common logarithm operation provided by the server - let _ = await Client(for: s) { c in + let _ = await Client(for: s) { e in // Chooses logarithms operations - await Session.right(c) { c in + await Session.right(e) { e in // Chooses common logarithm - await Session.right(c) { c in + await Session.right(e) { e in let number: Double = 100.0 // Approximation of e - await Session.send(number, on: c) { c in - await Session.recv(from: c) { result, c in - await Session.close(c) + await Session.send(number, on: e) { e in + await Session.recv(from: e) { result, e in + await Session.close(e) assert(result == 2.0) } } diff --git a/Tests/StandardTests.swift b/Tests/StandardTests.swift index 04b2dbe..ed1252c 100644 --- a/Tests/StandardTests.swift +++ b/Tests/StandardTests.swift @@ -15,18 +15,18 @@ final class StandardTests: XCTestCase { /// This test verifies the library behavior with a standard communication between two processess /// using the closure continuation-passing coding style func testIsEvenWithClosures() async { - await Session.create { c in + await Session.create { e in // One side of the communication channel - await Session.recv(from: c) { num, c in - await Session.send(num % 2 == 0, on: c) { c in - await Session.close(c) + await Session.recv(from: e) { num, e in + await Session.send(num % 2 == 0, on: e) { e in + await Session.close(e) } } - } _: { c in + } _: { e in // Another side of the communication channel - await Session.send(42, on: c) { c in - await Session.recv(from: c) { isEven, c in - await Session.close(c) + await Session.send(42, on: e) { e in + await Session.recv(from: e) { isEven, e in + await Session.close(e) assert(isEven == true) } } @@ -36,19 +36,19 @@ final class StandardTests: XCTestCase { /// This test verifies the library behavior with a standard communication between two processess /// using the channel passing coding style func testIsEvenWithPassing() async { - typealias Communication = Channel), Empty>)> + typealias Protocol = Endpoint), Empty>)> // One side of the communication channel - let c = await Session.create { (c: Communication) in - let (num, c1) = await Session.recv(from: c) - let c2 = await Session.send(num % 2 == 0, on: c1) - await Session.close(c2) + let e = await Session.create { (e: Protocol) in + let (num, e1) = await Session.recv(from: e) + let e2 = await Session.send(num % 2 == 0, on: e1) + await Session.close(e2) } // Another side of the communication channel - let c1 = await Session.send(42, on: c) - let (isEven, c2) = await Session.recv(from: c1) - await Session.close(c2) + let e1 = await Session.send(42, on: e) + let (isEven, e2) = await Session.recv(from: e1) + await Session.close(e2) assert(isEven == true) } @@ -57,29 +57,29 @@ final class StandardTests: XCTestCase { /// using the client/server architecture style func testIsEvenWithClientServer() async { // Server side - let s = await Server { c in - await Session.recv(from: c) { num, c in - await Session.send(num % 2 == 0, on: c) { c in - await Session.close(c) + let s = await Server { e in + await Session.recv(from: e) { num, e in + await Session.send(num % 2 == 0, on: e) { e in + await Session.close(e) } } } // Client side - let _ = await Client(for: s) { c in - await Session.send(42, on: c) { c in - await Session.recv(from: c) { isEven, c in - await Session.close(c) + let _ = await Client(for: s) { e in + await Session.send(42, on: e) { e in + await Session.recv(from: e) { isEven, e in + await Session.close(e) assert(isEven == true) } } } // Another client - let _ = await Client(for: s) { c in - await Session.send(3, on: c) { c in - await Session.recv(from: c) { isEven, c in - await Session.close(c) + let _ = await Client(for: s) { e in + await Session.send(3, on: e) { e in + await Session.recv(from: e) { isEven, e in + await Session.close(e) assert(isEven == false) } }