{"id":15992199,"url":"https://github.com/janstenpickle/otters","last_synced_at":"2025-08-11T17:12:52.874Z","repository":{"id":47845103,"uuid":"123625320","full_name":"janstenpickle/otters","owner":"janstenpickle","description":"Streaming cats","archived":false,"fork":false,"pushed_at":"2018-09-26T14:48:07.000Z","size":165,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-09T07:51:29.109Z","etag":null,"topics":["akka-streams","cats","fs2","monix","reactive-streams","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/janstenpickle.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-03-02T20:06:13.000Z","updated_at":"2024-05-19T12:38:25.000Z","dependencies_parsed_at":"2022-08-29T13:23:20.639Z","dependency_job_id":null,"html_url":"https://github.com/janstenpickle/otters","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/janstenpickle/otters","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janstenpickle%2Fotters","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janstenpickle%2Fotters/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janstenpickle%2Fotters/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janstenpickle%2Fotters/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/janstenpickle","download_url":"https://codeload.github.com/janstenpickle/otters/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janstenpickle%2Fotters/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269923372,"owners_count":24497102,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-11T02:00:10.019Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["akka-streams","cats","fs2","monix","reactive-streams","scala"],"created_at":"2024-10-08T06:05:59.538Z","updated_at":"2025-08-11T17:12:52.813Z","avatar_url":"https://github.com/janstenpickle.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Otters - [Cats](https://typelevel.org/cats/) in Streams\n[![Build Status](https://travis-ci.org/janstenpickle/otters.svg?branch=master)](https://travis-ci.org/janstenpickle/otters) [![codecov](https://codecov.io/gh/janstenpickle/otters/branch/master/graph/badge.svg)](https://codecov.io/gh/janstenpickle/otters)\n\n\nOtters uses the [Cats](https://typelevel.org/cats/) library to provide a few typeclasess and useful syntax for working with streaming libraries.\n\nOut of the box Otters supports\n- [Akka Streams](https://doc.akka.io/docs/akka/2.5/stream/index.html?language=scala)\n- [Monix Observable](https://monix.io/)\n- [Monix Iterant](https://monix.io/)\n- [FS2](https://functional-streams-for-scala.github.io/fs2/)\n\n# Background \u0026 Motivation\nAll streaming libraries share common concepts, Otters attempts to create typeclasses and corresponding laws based on [Typelevel Cats](https://typelevel.org/cats/) so that common patterns may be shared between all implementations, without masking their differences.\n\n# Concepts \nStreams provide a convienent way of expressing [coinductive](https://en.wikipedia.org/wiki/Coinduction) problems, meaning the input may never end, e.g. a web server.\n\nA Stream is essentially models an infinite collection of units of asynchronous work. Streams may be piped through other streams and sent to sinks.\n\n## Units of Execution\n\n### Stream\n\nA stream can be represented in the abstract as `F[A]` where `F` is the type specific to the library (see below) and `A` is the value type contained within that stream. E.g `Observable[Int]` in Monix.\n\n### Pipe\n\nA pipe can be represented `F[A] =\u003e F[B]` where `F` is a stream. This allows multiple streams to be composed.\n\n### Sink\n\nA sink can be represented `F[A] =\u003e G[B]` where `F` is a stream and `G` is the materialized value (see below).\n\n## Internal Types\n\n|Library|Stream Type|Asyncronous Type|Materialized Type|\n|-|-|-|-|\n|Akka Streams|`Source`|`Future`|`RunnableGraph`|\n|Monix Reactive|`Observable`|`Task`|`Task`|\n|Monix Tail|`Iterant`|Any `Effect`*|Any `Effect`*|\n|FS2|`Stream`|Any `Effect`*|Any `Effect`*|\n\n*`Effect` refers to [Cats Effect](https://github.com/typelevel/cats-effect/), where `IO` is the reference implementation, but this could be anything, for example Monix's `Task`.\n\n### Materialized Types\n\nNotice that only Akka Streams has a different materialized type. This is because `Future` is eagerly evaluated, meaning that the moment it is assigned to a value it will start doing its work. Eager evaluation is at odds with reactive streaming concepts, where the you can construct a graph from many logical parts and as a separate activity execute that graph as a fully connected stream. The type `RunnableGraph` represents lazy evaluation of the stream, only when `.run()` is called does the execution begin.\n\nMonix and FS2 are different in this respect as `Task` and `Effect` are lazily evaluated and therefore the materialized type can be the same as the asyncronous type used for each unit of work within the stream.\n\nFor example the result of sending a source to a sink which collects the result in a `Seq` in Akka Streams will be `RunnableGraph[Future[Seq[A]]]`, however because both the materialized and asychronous types in Monix and Fs2 are the same the effects can be captured in a single type, e.g. `Task[Seq[A]]` or `IO[Seq[A]]`.\n\n# Usage\n\n## Writer\nThe writer monad from Cats can be used in a streaming context to capture some state to be committed along with the main data at the end of the stream. This could be something like a Kafka offset or some stats regarding the execution.\n\n[more to follow]\n\n## Either\nThe either monad allows data to be routed via different pipes and to different sinks.\n\n[more to follow]\n\n# Participation\n\nThis project supports the Typelevel [code of conduct](http://typelevel.org/conduct.html) and aims that its channels\n(mailing list, Gitter, github, etc.) to be welcoming environments for everyone.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjanstenpickle%2Fotters","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjanstenpickle%2Fotters","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjanstenpickle%2Fotters/lists"}