• Stars
    star
    150
  • Rank 238,355 (Top 5 %)
  • Language
    Scala
  • Created over 1 year ago
  • Updated 11 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Direct-Style Programming for ZIO

ZIO Direct Style

ZIO Direct Style is a library that allows using directly-style i.e. imperative programming with ZIO effects which is based on the Monadless paradigm.

Development CI Badge Sonatype Releases Sonatype Snapshots javadoc ZIO Direct Style

Installation

To use zio-direct, add the following to your build.sbt file.

libraryDependencies += "dev.zio" %% "zio-direct" % "1.0.0-RC7"

You can also use zio-direct with ZStream and ZPure by importing the following modules.

Currently this is only supported in Scala 3.

// ZStream
libraryDependencies += "dev.zio" %% "zio-direct-streams" % "1.0.0-RC7"
// ZPure
libraryDependencies += "dev.zio" %% "zio-direct-pure" % "1.0.0-RC7"

See the Other Supported Monads section for more details.

IDE Support

The preferred IDE to use with ZIO-Direct is Visual Studio Code + Metals. This is because Metals correctly reads the returns from defer calls directly from the Scala compiler which is not the case of IntelliJ. To remedy this issue, a Library Extension is provided for ZIO-Direct. See the IntelliJ Support section for more details.

Introduction

Talk at Functional Scala 2022:

ZIO-Direct allows direct style programming with ZIO. This library provides a syntactic sugar that is more powerful than for-comprehensions as well as more natural to use. Simply add the .run suffix to any ZIO effect in order to retrieve its value.

ZIO-Direct works by using macros to rewrite sequential code into flatMap-chains based on the Monadless paradigm. The values resulting in .run calls from the ZIO effects are not actually awaited. Instead, they are rolled-up into a chain of flatMaps.

For example, in imperative programming operations typically are done in a simple set of steps.

object FileOps:
  def read(file: File): String
  def write(file: File, content: String): Unit

val textA = read(fileA)
val textB = read(fileB)
write(fileC, textA + textB)

Using functional programming, the equivalent of this functionality is a set of nested flatMap-chains.

object FileOps
  def read(file: File): ZIO[Any, Throwable, String]
  def write(file: File, content: String): ZIO[Any, Throwable, Unit]

read(fileA).flatMap { textA =>
  read(fileB).flatMap { textB =>
    write(fileC, textA + textB)
  }
}

In order to avoid this complexity scala provides a for-comprehension syntactic sugar.

for {
  textA <- read(fileA)
  textB <- read(fileB)
  _ <- write(fileC, textA + textB)
} yield ()

Unfortunately this syntactic sugar is limited in many cases, for example, inserting a conditional value inside is impossible.

for {
  textA <- read(fileA)
  // Not a possible syntax
  if (fileA.contains("some string")) {
    textB <- read(fileB)
    _ <- write(fileC, textA + textB)
  }
} yield ()

ZIO-Direct offers an equivalent syntactic sugar that is more ergonomic and allows many constructs that for-comprehensions do not.

defer {
  val textA = read(fileA).run
  if (fileA.contains("some string")) {
    val textB = read(fileB).run
    write(fileC, textA + textB).run
  }
}

ZIO-Tailored

ZIO-Direct is specifically tailored to ZIO capabilities as it supports Environment and Error composition in ZIO effects similar to the for-comprehension.

val out: ZIO[CustomerConfig & DistributorConfig, CustomerGetException | DistrubutorGetException, (Customer, Distributor)] =
  defer {
    // Get a customer-configuration object from the environment and extract its .url field
    val custUrl: String = ZIO.service[CustomerConfig].run.url
    // Get a distributor-configuration from the environment and extract its .url field
    val distUrl: String = ZIO.service[DistributorConfig].run.url
    (
      // Use the two configurations to make an HTTP-call
      parseCustomer(httpGetCustomer(custUrl).run),
      parseDistrubutor(httpGetDistributor(distUrl).run)
    )
  }

Branching and Looping Support

Unlike the for-comprehension, ZIO-Direct supports branching and looping in the use of flatMaps composition. Let's have a look at a another non-trivial example.

class Database:
  def nextRow(): ZIO[Any, Throwable, Row]
  def hasNextRow(): ZIO[Any, Throwable, Boolean]
  def lockNextRow(): ZIO[Any, Throwable, Boolean]
object Database:
  def open: ZIO[Any, Throwable, Database]

defer {
  // Open a database connection
  val db = Database.open().run
  // See if there is is a next-row
  while (db.hasNextRow().run) {
    // try to lock, if aquired continue
    if (db.lockNextRow().run)
      val nextRow = db.nextRow().run
      doSomethingWith(nextRow)
    else
      waitT()
  }
}

NOTE: The above database-api is imaginary.

The above code needs to be translated into something like this:

Database.open.flatMap { db =>
  def whileFun(): ZIO[Any, Throwable, Unit] =
    db.hasNextRow().flatMap { hasNextRow =>
      if (hasNextRow)(
        db.lockNextRow().flatMap { lockNextRow =>
          if (!lockNextRow)
            db.nextRow().map(nextRow => doSomethingWith(nextRow))
          else
            ZIO.succeed(waitT())
        }
      ).flatMap(_ => whileFun())
      else
        ZIO.unit
    }
  whileFun()
}

Note that normally this is the exact code that would have to be written to capture such functionality For-comprehensions do not provide a way to do looping and branching so in such cases a combination of flatMaps and recursion is necessary to avoid calling effects unnecessarily.

Great for Refs and FiberRefs!

ZIO-direct makes it much easier to use ZIO mutable Ref and FiberRef variables. Since retrieveing and updating Ref and FiberRef variables requries a flatMap/for-comprehension call, it is typically very difficult to use them with branching/looping constructs. ZIO-direct makes these cases much easier.

class DatabaseApi {
  val connRef = FiberRef.make[Option[Connection]](None)
  def openConnection(): Connection = lowLevelDatabaseApi.openConnection()
  def transaction(action: Action) =
    defer {
      val conn = connRef.get.run
      if (conn == None) {
        connRef.set(Some(openConnection())).run
      }
      val conn1 = connRef.get.run
      conn.execute(action).run
    }
}

Instead of having to write the following code:

class DatabaseApi {
  val connRef = FiberRef.make[Option[Connection]](None)
  def openConnection(): Connection = lowLevelDatabaseApi.openConnection()
  def transaction(action: Action) =
    connRef.get.flatMap { conn =>
      (if (conn == None) {
        connRef.set(Some(openConnection()))
      } else {
        ZIO.unit
      }).flatMap(_ => connRef.get.flatMap { conn1 =>
        conn1.execute(action)
      })
    }

  // Note that for-comprehensions do not help very much in this use-case
  def transaction0(action: Action) =
    for {
      conn <- connRef.get
      _ <- if (conn == None) {
        connRef.set(Some(openConnection()))
      } else {
        ZIO.unit
      }
      conn1 <- connRef.get
      _ <- conn1.execute(action)
    }
}

Documentation

Learn more on the ZIO Direct Style homepage!

Contributing

For the general guidelines, see ZIO contributor's guide.

Code of Conduct

See the Code of Conduct

Support

Come chat with us on Badge-Discord.

License

License

More Repositories

1

zio

ZIO — A type-safe, composable library for async and concurrent programming in Scala
Scala
3,984
star
2

zio-quill

Compile-time Language Integrated Queries for Scala
Scala
2,135
star
3

zio-http

A next-generation Scala framework for building scalable, correct, and efficient HTTP clients and servers
Scala
732
star
4

zio-prelude

A lightweight, distinctly Scala take on functional abstractions, with tight ZIO integration
Scala
437
star
5

zio-json

Fast, secure JSON library with tight ZIO integration.
Scala
401
star
6

zio-kafka

A Kafka client for ZIO and ZIO Streams
Scala
332
star
7

zio-actors

A high-performance, purely-functional library for building, composing, and supervising typed actors based on ZIO
Scala
266
star
8

zio-microservice

ZIO-powered microservices via HTTP and other protocols.
Scala
238
star
9

zio-sql

Type-safe, composable SQL for ZIO applications
Scala
233
star
10

zio-config

Easily use and document any config from anywhere in ZIO apps
Scala
223
star
11

zio-intellij

A companion IntelliJ IDEA plugin for the ZIO library ecosystem.
Scala
210
star
12

zio-keeper

A ZIO library for building distributed systems
Scala
198
star
13

zio-protoquill

Quill for Scala 3
Scala
195
star
14

zio-nio

A small, unopinionated ZIO interface to NIO.
Scala
188
star
15

zio-logging

Powerful logging for ZIO 2.0 applications, with compatibility with many logging backends out-of-the-box.
Scala
171
star
16

zio-akka-cluster

ZIO wrapper for Akka Cluster
Scala
164
star
17

interop-cats

ZIO instances for cats-effect type classes
Scala
158
star
18

zio-query

Add efficient pipelining, batching, and caching to any data source
Scala
145
star
19

zio-aws

Low level ZIO interface for the full AWS
Scala
137
star
20

zio-flow

Resilient, distributed applications powered by ZIO
Scala
136
star
21

izumi-reflect

TypeTag without scala-reflect. Supports Scala 2 and Scala 3.
Scala
134
star
22

zio-schema

Compositional, type-safe schema definitions, which enable auto-derivation of codecs and migrations.
Scala
133
star
23

zio-cli

Rapidly build powerful command-line applications powered by ZIO
Scala
126
star
24

zio-redis

A ZIO-based redis client
Scala
118
star
25

zio-telemetry

ZIO-powered OpenTelemetry library
Scala
109
star
26

zio-petclinic

An idiomatic pet clinic application written with ZIO.
Scala
102
star
27

zio-zmx

Monitoring, Metrics and Diagnostics for ZIO
Scala
85
star
28

zio-cache

A ZIO native cache with a simple and compositional interface
Scala
84
star
29

zio-sqs

ZIO-powered client for AWS SQS
Scala
83
star
30

zio-shield

Enforce best coding practices with ZIO
Scala
82
star
31

zio-jdbc

A small, idiomatic ZIO interface to JDBC.
Scala
80
star
32

zio-openai

Scala
65
star
33

zio-process

A simple ZIO library for interacting with external processes and command-line programs
Scala
64
star
34

zio-analytics

Distributed stream processing using ZIO
Scala
61
star
35

zio-metrics-legacy

⛔️ DEPRECATED
Scala
57
star
36

zio-quickstarts

A minimal quickstart ZIO application for writing a RESTful Web Service
Scala
56
star
37

zio-optics

Easily modify parts of larger data structures
Scala
56
star
38

interop-reactive-streams

Interoperability Layer Between ZIO and Reactive Streams
Scala
47
star
39

zio-s3

An S3 client for ZIO
Scala
41
star
40

zio-lambda

AWS Lambda Runtime built with ZIO
Scala
39
star
41

zio-dynamodb

Simple, type-safe, and efficient access to DynamoDB
Scala
38
star
42

zio-amqp

ZIO-based AMQP client for Scala
Scala
33
star
43

zio-connect

Sources, Sinks and Pipelines for channeling data
Scala
33
star
44

zio-macros

Macros to scrap ZIO boilerplate
Scala
32
star
45

zio-rocksdb

A ZIO-based interface to RocksDB.
Scala
32
star
46

zio-metrics-connectors

Monitoring, Metrics and Diagnostics for ZIO
Scala
30
star
47

zio-ftp

A simple, idiomatic (S)FTP client for ZIO
Scala
30
star
48

zio-crypto

Fast, secure cryptographic primitives in a ZIO & ZIO Streams friendly package.
Scala
29
star
49

zio-parser

Scala
28
star
50

zio-mock

Scala
27
star
51

zio-constraintless

An advanced library for building DSLs that allows defering the existence of type class instances until interpretation.
Scala
26
star
52

zio-codec

High-performance codecs for ZIO applications
Scala
23
star
53

zio-project-seed.g8

giter8 template used to start new ZIO projects for the ZIO organization
Scala
22
star
54

zio-webhooks

A microlibrary for reliable and persistent webhook delivery
Scala
20
star
55

zio-lambda-old

ZIO-powered custom runtime for AWS Lambda
Scala
19
star
56

zio-profiling

Scala
18
star
57

zio-gcp

A ZIO-based interface to Google Cloud API
Scala
18
star
58

zio-insight-ui

TypeScript
17
star
59

interop-java

Scala
17
star
60

caliban-deriving

Full-featured, robust deriving for Caliban.
Scala
16
star
61

zio-insight

Toolset for ZIO developers
Scala
16
star
62

zio-test-intellij

An optional ZIO Test runner support module for the ZIO IntelliJ plugin
Shell
16
star
63

zio-delegate

Scala
14
star
64

interop-twitter

Scala
14
star
65

zio-wasm

WASM AST and syntax based on zio-parser.
Scala
14
star
66

zio-deriving

Scala
11
star
67

zio-memberlist

Cluster membership and failure detection
Scala
11
star
68

zio-sbt

SBT Plugins For ZIO Projects
Scala
11
star
69

interop-monix

Scala
8
star
70

zio-spark

A simple, type-safe ZIO interface to Spark
Scala
8
star
71

interop-scalaz

Scala
7
star
72

interop-guava

Scala
6
star
73

zio-concurrent

Concurrency utilities for ZIO.
JavaScript
6
star
74

zio-morphir

Scala
5
star
75

zio-uring

Scala
5
star
76

interop-future

Scala
5
star
77

zio-quickstart-graphql-webservice

Quickstart for Writing GraphQL Servers
Scala
4
star
78

zio-cron

ZIO Cron
3
star
79

zio-bson

BSON library with tight ZIO integration
Scala
3
star
80

zio-simple-seed.g8

Scala
3
star
81

zio-meta

Scala
2
star
82

zio-quickstart-hello-world

A minimal working example of ZIO Application
Scala
2
star
83

zio-direct-intellij

Scala
2
star
84

zio-insight-server

Scala
2
star
85

zio-distributed

The future home of ZIO Distributed!
Scala
1
star
86

zio-docs

Experimenting with a new way of maintaining dev.zio
JavaScript
1
star