# FTL Primitives This document describes the primitives provided by FTL. These primitives can be composed # Verbs, sources, and sinks These are the function primitives of FTL. ## Verbs Verbs take a single value and return a single value or an error. ``` F(X) -> Y ``` <details> <summary>Code</summary> ```go func F(ctx context.Context, x X) (Y, error) { // ... } ``` </details> ## Sources Sources are functions that return a single value and accept no parameters. Common use cases for sources are providing data from a queue or external system. ``` F() -> Y ``` <details> <summary>Code</summary> ```go func F(ctx context.Context) (Y, error) { // ... } ``` </details> ## Sinks Sinks are functions that accept values but return nothing. They will typically have some side-effect such as delivering to a queue or external system. ``` F(X) ``` <details> <summary>Code</summary> ```go func F(ctx context.Context, req X) error { // ... } ``` </details> # Messaging patterns ## Synchronous A synchronous call to F(X) -> Y executes the function and blocks the caller until the result or an error is returned. ``` F(X) -> Y ``` <details> <summary>Code</summary> ```go func F(ctx context.Context, req X) (Y, error) { // ... } func G(ctx context.Context, x X) (Y, error) { // Synchronously call another verb. if y, err := ftl.Call(ctx, F, x); err != nil { return VerbResponse{}, err } // ... } ``` </details> ## PubSub PubSub in FTL is comprised of four functions: 1. A (sink) topic T(X) that messages can be published to. 2. One or more (source) subscriptions S^n^() -> X for each topic. Each subscription is guaranteed to return the next message from the topic. 3. One or more subscribers F^n^(X) for each subscription. 4. A single, optional user-defined function F(E) for handling failed messages. This may be a dead letter topic. Other notes: - A message of type X may be published to a PubSub topic T. - An arbitrary number of subscriptions can be created for a topic. - Multiple functions can be configured to consume from a single subscription, first-come first-served. - FTL PubSub is implemented on top of a Kafka-like event log. - Different toplogies are just different combinations/configurations of topic retention and subscriptions. - A subscription is a cursor over the topic's event log. - Other asynchronous messaging patterns such as callbacks can be implemented on top of PubSub. - Batching can be achieved by defining topics/subscribers that accept a slice of X. <details> <summary>Code</summary> Declaring a Topic: ```go var T = ftl.CreateTopic[X]("topic_x") // Returns a Topic[T] ``` Creating and subscribing to a subscription: ```go var S = ftl.CreateSubscription(T, "subscription_x", F) //ftl:sink func F(ctx context.Context, x X) error { // ... } ``` Publishing to a topic ```go //ftl:verb func Verb(ctx context.Context, x X) error { if err := ftl.Publish(ctx, T, x); err != nil { return Y{}, err } // ... } ``` </details> ### Fanout In the fanout topology, a copy of each message published to a topic is received by every subscriber. For this pattern, a separate subscription `Sn` exists for each subscriber `Fn`: ``` T(X0) -> S0(X0) -> F0(X0) S1(X0) -> F1(X0) ... T(X1) -> S0(X1) -> F0(X1) S1(X1) -> F1(X1) ... ``` <details> <summary>Code</summary> ```go var T = ftl.CreateTopic[X]("T") var S0 = ftl.Subscription(T, "S0", F0) var S1 = ftl.Subscription(T, "S1", F1) //ftl:sink func F0(ctx context.Context, msg T) error { // ... } //ftl:sink func F1(ctx context.Context, msg T) error { // ... } ``` </details> ### First-come first-served In the first-come first-served topology, a single subscription exists for each topic and each message published to a topic is sent to the next available subscriber. Given a single subscription and two subscribers F0 and F1, this might look like: ``` T(X0) -> S(X0) -> F0(X0) ``` ``` T(X1) -> S(X1) -> F1(X1) ``` ``` T(X2) -> S(X2) -> F1(X2) ``` <details> <summary>Code</summary> ```go var T = ftl.CreateTopic[X]("T") var S = ftl.CreateSubscription(T, "S", F0, F1) //ftl:sink func F0(ctx context.Context, msg T) error { // ... } //ftl:sink func F1(ctx context.Context, msg T) error { // ... } ``` </details>