{"id":20595586,"url":"https://github.com/johnpaulada/baccano","last_synced_at":"2025-04-14T23:43:33.583Z","repository":{"id":44331551,"uuid":"138887172","full_name":"johnpaulada/baccano","owner":"johnpaulada","description":"🛤️ A railway-oriented programming helper library.","archived":false,"fork":false,"pushed_at":"2023-07-11T14:06:06.000Z","size":54,"stargazers_count":11,"open_issues_count":10,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-14T23:43:28.027Z","etag":null,"topics":["javascript","railway-oriented-programming"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/johnpaulada.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-06-27T13:42:48.000Z","updated_at":"2023-06-14T22:25:53.000Z","dependencies_parsed_at":"2022-09-01T16:51:40.404Z","dependency_job_id":null,"html_url":"https://github.com/johnpaulada/baccano","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnpaulada%2Fbaccano","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnpaulada%2Fbaccano/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnpaulada%2Fbaccano/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnpaulada%2Fbaccano/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/johnpaulada","download_url":"https://codeload.github.com/johnpaulada/baccano/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248981259,"owners_count":21193143,"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":["javascript","railway-oriented-programming"],"created_at":"2024-11-16T08:13:34.831Z","updated_at":"2025-04-14T23:43:33.564Z","avatar_url":"https://github.com/johnpaulada.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Baccano\nA railway-oriented programming helper library.\n\n[![forthebadge](https://forthebadge.com/images/badges/fuck-it-ship-it.svg)](https://forthebadge.com)\n[![forthebadge](https://forthebadge.com/images/badges/made-with-javascript.svg)](https://forthebadge.com)\n[![forthebadge](https://forthebadge.com/images/badges/powered-by-electricity.svg)](https://forthebadge.com)\n\n[![npm](https://img.shields.io/npm/dm/baccano.svg?longCache=true\u0026style=for-the-badge)](https://www.npmjs.com/package/baccano)\n[![npm](https://img.shields.io/npm/v/baccano.svg?longCache=true\u0026style=for-the-badge)](https://www.npmjs.com/package/baccano)\n[![](https://data.jsdelivr.com/v1/package/npm/baccano/badge)](https://www.jsdelivr.com/package/npm/baccano)\n\n## Intro to Railway-oriented Programming\nThe term was coined by Scott Wlaschin of F# for Fun and Profit. [Here's his talk on the subject](https://fsharpforfunandprofit.com/rop/). This method of programming allows us to deal with errors functionally in our applications. It involves having two paths (or tracks) if you will, one is the success path, and one is the error path. You start with the success path and when an error occurs, expected or not, you move to the error path. It's easier to understand if you try it out.\n\n## Getting Started\n\n### Importing the library\n\nTo use the library, first import it:\n\nIn Node:\n```javascript\nconst { compose, fromUnary, SomeError, Success } = require('baccano')\n```\n\nAs ES Module:\n```javascript\nimport { compose, fromUnary, SomeError, Success } from 'baccano'\n```\n\nOn the browser:\n```html\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/baccano@2.1.1/baccano.min.js\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n    const { compose, fromUnary, SomeError, Success } = Baccano\n\u003c/script\u003e\n```\n\n### Using the library\nLet's learn how to use the library by creating a pipeline of mathematical operations.\n\n#### Define Errors\nFeatures should not be the only things to be considered when planning software. Errors, or anything that can go wrong should also be planned. Hence, we have to define the possible errors that might occur in a particular pipeline of functions. Normally we'd use a type or a variant for this but we're in JavaScript so I suggest using Symbols for them. We'll be using division in the pipeline so we have to plan for a division by zero case.\n\n```javascript\nconst DIVISON_BY_ZERO = Symbol.for('DIVISION_BY_ZERO')\n```\n\n#### Define Functions\nNow we'll define the functions that we will use. In Railway-Oriented Programming, a function should either return a success with the value, or some error with the error message. This would be easy in type-safe functional programming languages but that is not the case with JavaScript so we'll need some helpers from the library.\n\n```javascript\nimport { SomeError, Success } from 'baccano'\n```\n\nAfter importing our helper functions, let's define the functions we're going to use.\n\n```javascript\nconst divideBy = n =\u003e x =\u003e {\n  if (n === 0) {\n    return SomeError(DIVISON_BY_ZERO, \"Cannot divide by zero.\")\n  } else {\n    return Success(n / x)\n  }\n}\n\nconst plusOne = x =\u003e {\n  return Success(x + 1)\n}\n```\n\nIn this case, we create a function `divideBy` which takes a number and returns a function that accepts a number and divides it by the previous number. If the previous number is zero, we return a `DIVISON_BY_ZERO` error using the `SomeError` function, which takes a value that represents the error and the error message. Else, we return a success using the `Success` function which accepts a value.\n\nThe `plusOne` function just takes a number and returns a `Success` with the number incremented by one.\n\n#### Convert to ROP-compatible functions\nIf you noticed, our functions accept a single value and return a single value, which could either be success or error.\nIn Railway-Oriented Programming, functions should accept two values and return two values, which represent the happy/success path and the error path. So we have to convert them into compatible functions. For that, we need the `fromUnary` function.\n\n```javascript\nimport { fromUnary, SomeError, Success } from 'baccano'\n\nconst compatibleDivideByZero = fromUnary(divideBy(0))\nconst compatiblePlusOne = fromUnary(plusOne)\n```\n\nNow we can use these functions in our pipeline.\n\n#### Composing functions into a pipeline\nNow that we have compatible functions, let's compose them into a single function using the `pipeline` function.\n\n```javascript\nimport { fromUnary, pipeline, SomeError, Success } from 'baccano'\n\nconst pipeline = compose(compatiblePlusOne, compatibleDivideByZero, compatiblePlusOne)\n```\n\nThis `pipeline` function is an asynchronous function which accepts a value and returns a Promise that resolves into the value after it has been run through the series of functions.\n\nWe run the pipeline like this:\n\n```javascript\n(async () =\u003e {\n\n  // Get result of the pipeline\n  const result = await pipeline(2)\n\n  // Display end value\n  console.log(result.value) // 4\n\n  // Display errors\n  console.log(result.errors) // [ { message: 'Cannot divide by zero.', type: Symbol(DIVISON_BY_ZERO) } ]\n})()\n```\n\nThat's it for this example!\n\n#### Complete Example Code\nHere is the complete example code:\n\n```javascript\n\n// Import library\nimport { compose, fromUnary, SomeError, Success } from 'baccano'\n// or const { compose, fromUnary, SomeError, Success } = Baccano\n\n// Define Errors\nconst DIVISON_BY_ZERO = Symbol.for('DIVISION_BY_ZERO')\n\nconst divideBy = n =\u003e x =\u003e {\n  if (n === 0) {\n    return SomeError(DIVISON_BY_ZERO, \"Cannot divide by zero.\")\n  } else {\n    return Success(n / x)\n  }\n}\n\nconst plusOne = x =\u003e {\n  return Success(x + 1)\n}\n\n// Take unary functions and convert them to compatible functions\nconst compatibleDivideByZero = fromUnary(divideBy(0))\nconst compatiblePlusOne = fromUnary(plusOne)\n\n// Create pipeline of functions\nconst pipeline = compose(compatiblePlusOne, compatibleDivideByZero, compatiblePlusOne)\n\n(async () =\u003e {\n\n  // Get result of the pipeline\n  const result = await pipeline(2)\n\n  // Display end value\n  console.log(result.value) // 4\n\n  // Display errors\n  console.log(result.errors) // [ { message: 'Cannot divide by zero.', type: Symbol(DIVISON_BY_ZERO) } ]\n})()\n```\n\n## Notes\n- I named the library Baccano because when I thought about trains and railways, I thought of the Baccano anime. [@egoist](https://github.com/egoist) is not the only one fond of anime references LOL.\n\n## Roadmap\n- [x] Handling Asynchronous functions\n- [ ] Parallel execution\n- [ ] Lazy execution\n\n## License\nMIT","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnpaulada%2Fbaccano","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohnpaulada%2Fbaccano","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnpaulada%2Fbaccano/lists"}