import cats.effect.IO | |
import fs2.{Pipe, Pull, Stream} | |
object JsonlPayloadPipe { | |
/** Builds a pipe that transforms a stream of JSON strings into payloads for the Elasticsearch Bulk | |
* API. Each input string represents a JSON document to be sent to the Bulk API. The JSONs are | |
* gathered, interspersed with `delimiter`, and packaged into payload strings up to a maximum | |
* size of `batchMax` bytes. | |
* |
This is intended to be a realistic benchmark for a typical production microservice. Each module builds to a Docker container which is intended to be deployed in an autoscaling cluster topology (usually Kubernetes) with mocked upstreams. All measurements are intended to be end-to-end using a scaled test harness like Gatling. In a meaningful sense, this is intended to be a more representative alternative to measurement frameworks such as TechEmpower.
Each module contains a fully independent, idiomatic, and independently-tuned implementation of the same service within a different ecosystem. Pull requests welcome! I'm not personally equally familiar with all of the different frameworks and languages represented within this repository, so if you see something that could be more optimal and/or idiomatic, please feel free to make the change! With that said, the goal is for all implementations to be roughly on the same level in terms
package me.panavtec.scalacheck | |
import org.scalacheck.Arbitrary | |
import org.scalacheck.Gen | |
import scala.compiletime.{erasedValue, summonInline} | |
import scala.deriving.* | |
object ArbitraryDeriving: |
What follows are some of my (very) rough thoughts on what we can and should do with respect to CPS transformation in Scala at the language level. I'll try to start with some motivation behind my thinking, as well as some rambling observations on the nature of the problem space, but don't expect too much coherence here. :-)
Async programming is hard.
Okay let's actually be more specific than that. High-performance I/O is hard. Signal multiplexing is a powerful technique for achieving high(er) performance I/O, particularly network I/O, but the tradeoff is that, in order to utilize it, the user-space programming model must allow for suspension and resumption of sequential continuations (often called "fibers" or "coroutines"). Achieving this type of programming model without significant tradeoffs in usability is what is exceptionally hard.
If that wasn't bad enough though, these problems are inextricably conflated with another set of problem spaces which are, themselves, very difficult. In
Think of all the arguments you've heard as to why static typing is desirable — every single one of those arguments applies equally well to using types to represent error conditions.
An odd thing I’ve observed about the Scala community is how many of its members believe that a) a language with a sophisticated static type system is very valuable; and b) that using types for error handling is basically a waste of time. If static types are useful—and if you like Scala, presumably you think they are—then using them to represent error conditions is also useful.
Here's a little secret of functional programming: errors aren't some special thing that operate under a different set of rules to everything else. Yes, there are a set of common patterns we group under the loose heading "error handling", but fundamentally we're just dealing with more values. Values that can have types associated with them. There's absolutely no reason why the benefits of static ty
The purpose of this document is to specify a baseline set of functional programming features that users can request and that programming languages can advertise support for. This feature set strives to be "JSON-like" and "purely functional".
The goals of this specification are (in descending order of importance):
- Cultivate a portable functional programming style
package com.myproject.prelude | |
import cats.syntax.{AllSyntaxBinCompat => CatsSyntax} | |
import cats.effect.syntax.{AllSyntax => CESyntax} | |
import cats.effect.instances.{AllInstances => CEInstances} | |
/** Custom prelude for importing with -Yimport | |
* | |
* This means we never need to import cats syntax or stream explicitly | |
*/ |
import java.util.UUID | |
import monocle.Iso | |
trait IsUUID[A]: | |
def iso: Iso[UUID, A] | |
object IsUUID: | |
def apply[A](using ev: IsUUID[A]): IsUUID[A] = ev |
Why you prefer cats instead of zio? TF? It’s looks like zio ecosystem more widely and zio 2.0 has better performance What do you think?
Great question!
Performance-wise, it really depends on what you're doing. The problem with benchmarks (including the ones posted for ZIO and Cats Effect) is that they apply only to abstract situations, which are often nothing like what you see in real applications. A great write-up on this problem by Daniel Spiewak here, he wrote it better than I ever could: https://gist.github.com/djspiewak/f4cfc08e0827088f17032e0e9099d292
Also, this is not an app meant for production - I don't care that much about performance under load because there will be no load. And individual operations will often be bounded by I/O anyway, so the efficiency of the underlying runtime is likely not going to make a noticeable difference in use. Then again, both the client and server are JVM apps, so even the start-up penalty of the client will slow us down than picking even the least efficient e