{"id":16482289,"url":"https://github.com/paulpdaniels/streamish","last_synced_at":"2025-02-28T19:41:52.965Z","repository":{"id":57372177,"uuid":"93113612","full_name":"paulpdaniels/streamish","owner":"paulpdaniels","description":"A simple Functional Reactive Streaming library","archived":false,"fork":false,"pushed_at":"2017-08-22T15:00:57.000Z","size":82,"stargazers_count":0,"open_issues_count":8,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-27T00:05:37.011Z","etag":null,"topics":["fp","functional-programming","reactive","streams"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/paulpdaniels.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-06-02T01:16:42.000Z","updated_at":"2017-07-08T16:31:27.000Z","dependencies_parsed_at":"2022-09-10T01:20:56.630Z","dependency_job_id":null,"html_url":"https://github.com/paulpdaniels/streamish","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paulpdaniels%2Fstreamish","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paulpdaniels%2Fstreamish/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paulpdaniels%2Fstreamish/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paulpdaniels%2Fstreamish/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/paulpdaniels","download_url":"https://codeload.github.com/paulpdaniels/streamish/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241214107,"owners_count":19928243,"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","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":["fp","functional-programming","reactive","streams"],"created_at":"2024-10-11T13:10:17.054Z","updated_at":"2025-02-28T19:41:52.930Z","avatar_url":"https://github.com/paulpdaniels.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# streamish\nA simple Functional Reactive Streaming library\n\n[![CircleCI](https://img.shields.io/circleci/project/github/paulpdaniels/streamish.svg)](https://github.com/paulpdaniels/streamish)\n[![Codecov](https://img.shields.io/codecov/c/github/paulpdaniels/streamish.svg)](https://github.com/paulpdaniels/streamish)\n[![npm](https://img.shields.io/npm/v/streamish.svg)](https://www.npmjs.com/package/streamish)\n[![npm](https://img.shields.io/npm/l/streamish.svg)]()\n[![David](https://img.shields.io/david/paulpdaniels/streamish.svg)]()\n[![David](https://img.shields.io/david/dev/paulpdaniels/streamish.svg)]()\n\n#### Install\n\n```bash\n\nnpm install --save streamish\n\n```\n\n## Warning! Work in progress!\n\nThis is currently a work in progress and more what I would consider to be an \"experiment\" than anything else. If you want a production ready system I would suggest checking out RxJS, XStreams, Most.js or Bacon.\n\nIf you want to help out and contribute I would greatly appreciate that too.\n\n#### Why another library?\n\nThe goal of this project is to be as modular as possible, so to that end I am not following what has become the standard in Reactive Programming libraries and making everything fluent. It is a useful pattern, and in the future I may add a module to do it if it can be done well, but my main goal with this library is both to learn a little more about the topic by creating a library from scratch and to make operators shippable similar to how `lodash` ships.\n\n\n### Getting started\n\nThe versatility of this library is in the flexibility with which you can define operations.\n\n```javascript\n\nimport {subscribe, Stream, fromEvent, fromTimer, delay, pipe, filter, map} from 'streamish';\n\n// Declare a sink which receives values\nconst consoleSink = subscribe(\n  value =\u003e console.log(value)\n);\n\n// Declare a stream which emits values\nconst streamFromArray = Stream([1, 2, 3, 4]);\nconst streamFromIterable = Stream('streams are fun!');\nconst streamFromPromise = Stream(Promise.resolve(42));\nconst streamFromOtherObservables = Stream(Rx.Observable.timer(2000));\n\n// Or use a factory method\nconst streamFromEvent = fromEvent(window, 'click');\nconst streamFromTimer = fromTimer(2000);\n\n// Build some logic\nconst pipeline = pipe(\n  filter(x =\u003e x % 2 == 0),\n  map(x =\u003e x * 2),\n  delay(100)\n);\n\n\n// Compose that logic\n\n// Stream + operators = Stream\nconst augmentedStream = pipeline(streamFromArray)\n\n// Operators + Operators = Operator\nconst convertCharsPipeline = map(ch =\u003e ch.charCodeAt(0));\n\n// Now can convert to numbers\nconst augmentedPipeline = pipe(convertCharsPipeline, pipeline);\n\n// Operators + Subscribe = Streamish\n\n// Will go live as soon as it is subscribed to\nconst subscribedPipeline = pipe(pipeline, consoleSink);\n\n//i.e. subscribedPipeline(streamFromPromise);\n\n\n```\n\n#### More on style\n\nRegardless of the style, operators will be imported as modules and composed together.\n\n```javascript\nconst Stream = require('streamish/Stream');\nconst map = require('streamish/operators/map');\nconst subscribe = require('streamish/operators/subscribe');\n\nsubscribe(next =\u003e console.log('Value: ' + next))(map(a =\u003e a * a)(Stream([1, 2, 3, 4])));\n```\n\nNow you may be saying to yourself, well that isn't particularly readable, so I'll stick to my fluent operators thank you very much.\n\nBut wait there is more!\n```javascript\n\nconst pipe = require('streamish/operators/pipe');\n\npipe(\n map(a =\u003e a * a),\n subscribe(next =\u003e console.log('Value: ' + next)\n)(Stream([1, 2, 3, 4]);\n\n```\n\nIf that looks familiar to you, Congratulations! You have probably used something like `Ramda` before which is where most of this library got its inspiration, in fact the design of `pipe` and its sibling `compose` are compatible with signatures from Ramda (*mostly*), and hopefully soon the FantasyLand Spec.\n\n#### Anatomy of an operator\n\nThe operators in Streamish all use a very common functional concept known as function currying. Without getting too much into the weeds of functional programming, currying is just a way of breaking up arguments that are passed to a function. They create what is referred to as a thunk which is a function that when called returns another function. As such every operator that you will encounter will be of the shape:\n\n```javascript\n\nconst someOperator = (...args) =\u003e (flow: Flow, scheduler: Scheduler) =\u003e Flow\n\n// Subscribe is a little special because it has a different return value\nconst subscribeLike = (...args) =\u003e (flow: Flow, scheduler: Scheduler) =\u003e Subscription\n\n```\n\nWhy do we do this? For one, it makes it much easier to combine operators because be push the operator specific stuff into the first functions arguments. The function that we get back from the initial call always has the same interface. This is what enables the functional chaining between operators. And it also allows us to do something that isn't normally possible which is *schedule everywhere*. Because fundamentally every operator passes along a reference to the scheduler that was passed into it, it is available to every internal operator that we subsequently call. \n\n** TODO ** Define opt-out behavior of using scheduler pass-through.\n\n#### Why is suscribe an operator too?\n\nWell quite simply because it makes sense. The Streams in this library are meant to be the bare metal requirement that fulfills the TC39 Observable specification, and would technically make them interoperable with other Observable libraries. By packaging `subscribe` as its own operator we can also do some other cool things.\n\nLet's look first at what the standard Stream subscribe signature looks like:\n\n```javascript\n\nconst subscription = Stream([1, 2, 3, 4]).subscribe({\n  next: x =\u003e console.log(x),\n  error: e =\u003e console.log(e),\n  complete: () =\u003e console.log('Done')\n});\n\n```\n\nUsing the raw Observable (or `Flow` as we call it in this library) requires a fully functional Observer. Why? Because this lets us build other capabilities on top of it. Basically using the raw form requires that the `Observer` be well formed, because it prevents us from having to do a bunch of other object creation under the covers to make your Observer into a proper Observer. If we allow `subscribe` to exist in its own right, then we can allow library consumers, or other libraries to define the kind of assumptions they want to be able to make about Observers.\n\nThus we could easily define a \"safe\" subscribe method which handles various conversion logic and creates a specific type of `Sink`.\n\n```javascript\n\n// A different subscribe method!\nconst subscribe = require('otherLibrary/operators/safeSubscribe');\n\npipe(\n map(a =\u003e a * a),\n subscribe(next =\u003e console.log('Value: ' + next)\n)(Stream([1, 2, 3, 4]);\n\n```\n\nNotice that we were able to swap the module **in place** because the subscribe is not linked to the `Observable` itself.\n\n#### Previous Work\n\n- [RxJS](https://github.com/ReactiveX/rxjs/)\n- [Most](https://github.com/cujojs/most)\n- [XStream](https://github.com/staltz/xstream)\n- [Kefir](https://rpominov.github.io/kefir/#)\n- [Bacon](https://baconjs.github.io/)\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaulpdaniels%2Fstreamish","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpaulpdaniels%2Fstreamish","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaulpdaniels%2Fstreamish/lists"}