{"_id":"@baransu/bs-let","_rev":"1-ee45dfeb4c7fa14e186270a31fc258af","name":"@baransu/bs-let","dist-tags":{"latest":"0.1.14-rc1"},"versions":{"0.1.14-rc1":{"name":"@baransu/bs-let","version":"0.1.14-rc1","description":"A PPX for async/await (and general monadic binding) in ReasonML.","repository":{"type":"git","url":"git+https://github.com/reasonml-labs/bs-let.git"},"license":"MIT","scripts":{"postinstall":"node ./copyPlatformBinaryInPlace.js"},"publishConfig":{"access":"public"},"gitHead":"ae6d24b94a42e405ab6a49825840c3f5fc794f65","bugs":{"url":"https://github.com/reasonml-labs/bs-let/issues"},"homepage":"https://github.com/reasonml-labs/bs-let#readme","_id":"@baransu/bs-let@0.1.14-rc1","_nodeVersion":"12.14.1","_npmVersion":"6.13.4","dist":{"integrity":"sha512-8qKD4MHBJlY4ttlhMsnfwuU7/ViHAL9of7AZpBf3LvuXbeOMxbtU4ggR152whT1zYYWh8nBAXkI1s5Qr9ADo7w==","shasum":"cec821c1c855b0c8f168e128a0c607a51f6db311","tarball":"https://registry.npmjs.org/@baransu/bs-let/-/bs-let-0.1.14-rc1.tgz","fileCount":52,"unpackedSize":39059762,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeHyzMCRA9TVsSAnZWagAA7HgQAIjat+mtSWJZ/WL5u/6U\nVl4ccu/U6jtsvCxNN2a9z0a8YulxJlLVZ8OuLgTjdz7hqMoER9XFMWnOXCzR\nahqBBaq3+UZ6BSzBzzHKlEPTzq1kzzMWcKUGos8+nwP8nER4hXwd869f5abu\nuG8dZ4ePnK47QHzyeLNgQ/na3kcxkBa8gnns++ci4HheABBbyvRrhdlF6LsC\ngAazN6R9dsmATOOy4obp8ljt0MEEvpEuFg1omp8VMFW97IOfoi5wNF/Am/Vd\ns99/ZhtDf8tnmdEcK/poiDmPdzl8HLGkG37GjgUnPSvRY9atGF25W/4/sz0X\n1ERnfXn9WNukKxY3z8/MUAVYMXWUA/wAHTTEYIsDZpYLg89zyaOtgjgUhJ35\nSm4H6u4xUZRrS8GkYBaqyWtSAhdBUJeIWAV1vEvK9UhRTwrgwt5SPkJGGXF4\nIo5AFWuJEeDh3tWjQsb0wqZFKq/R2+/5t7WKJP8i1kTb0CEny/PV1BqskVrA\nEg39JtjweKtg+dx7LPJf4SteNzHpNVHHj5/Zx8gnhYUnLdT+qBRSg0qmckH+\ndi9PGVqR3jTqokER52oGrJ+aMq5D5gz2drBClBZfCHAVuKvtaKePAN4m1dt6\nCnJjoBBzgLWKqY6BTeYN0soag0Lyl4dX70zNm8Smh1wYM+hl/c2vd+8Hc1S8\nn5Vm\r\n=E0YU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF7HJbWOErsaoQoAfCAaho1zrS6rOMv2C0etIkY985o8AiA8wBIwLPT9vs8k+ZNOl9D2jrp7AbWjqNubDT6vuogO0g=="}]},"maintainers":[{"name":"baransu","email":"tomaszcichocinski@gmail.com"}],"_npmUser":{"name":"baransu","email":"tomaszcichocinski@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/bs-let_0.1.14-rc1_1579101387330_0.09894284130374942"},"_hasShrinkwrap":false}},"time":{"created":"2020-01-15T15:16:27.303Z","0.1.14-rc1":"2020-01-15T15:16:27.755Z","modified":"2022-04-04T17:43:01.926Z"},"maintainers":[{"name":"baransu","email":"tomaszcichocinski@gmail.com"}],"description":"A PPX for async/await (and general monadic binding) in ReasonML.","homepage":"https://github.com/reasonml-labs/bs-let#readme","repository":{"type":"git","url":"git+https://github.com/reasonml-labs/bs-let.git"},"bugs":{"url":"https://github.com/reasonml-labs/bs-let/issues"},"license":"MIT","readme":"# bs-let\n\n![A woman knitting some code](https://github.com/reasonml-labs/bs-let/raw/master/artwork/eastwood-page-under-construction.png)\n\nThis is a PPX (language extension) designed to make _monadic operations_ (think async functions with \"await\" statements in Javascript if you don't know what a monadic operation is) easy to write and read in ReasonML.\n\n## Warning: Experimental Project\n\nThis is package is an experimental community project (a.k.a \"labs\"). This means that community members use it and contribute to it, but it is not officially supported or recommended by the ReasonML community. Using this project in a production environment means being willing to contribute yourself if needs be.\n\nAlso, the expected lifetime of this PPX is relatively short. OCaml 4.08 has introduced native syntax for doing the same thing that this PPX does (`let+`). At the time of this writing, Bucklescript is still a ways out from supporting OCaml 4.08, or backporting support for `let+`. This PPX has two purposes:\n\n- Provide a temporary solution until Bucklescript and Reason both support `let+`.\n- Gauge community interest in monadic syntax sugar in general and gather feedback on its usage.\n\n## Compatibility\n\nThis package only works with bs-platform 6.x and above. If you're stuck on 5.x take a look at [Jared's original repo](https://github.com/jaredly/let-anything).\n\n## Installation\n\n- `npm install --save-dev bs-let`\n- Open up your `bsconfig.json` and add `bs-let/ppx` to your `ppx-flags`. It should look something like this:\n\n  ```json\n  {\n    \"ppx-flags\": [\"bs-let/ppx\"]\n  }\n  ```\n\n## Usage\n\nSimple and sweet, this is a language extension that flattens callbacks.\n\nIf you'd like to see it in action, take a look at the video below. Otherwise, keep reading!\n\n[![Screenshot of instructional video from egghead.io](https://user-images.githubusercontent.com/1227109/70068564-76825580-15ad-11ea-8d4a-f2161bbd40f3.png)](https://egghead.io/lessons/reason-async-await-in-reasonml)\n\nAll you need is a module which defines a function called `let_` which takes something to map over, and a callback to do the mapping. For example:\n\n```reason\nmodule Option = {\n  let let_ = Belt.Option.flatMap;\n};\n```\n\nThen, when you're working with something you want to map, add a `%<ModuleName>` onto your `let`, and the rest of the lines in the block will be turned into a callback and passed to the mapping function _at compile time_.\n\nFor example:\n\n```reason\n// Assume the `Option` module from above is defined already.\n\ntype address = {street: option(string)};\n\ntype personalInfo = {address: option(address)};\n\ntype user = {info: option(personalInfo)};\n\n// Get the user's street name from a bunch of nested options. If anything is\n// None, return None.\nlet getStreet = (maybeUser: option(user)): option(string) => {\n  let%Option user = maybeUser;\n  // Notice that info isn't an option anymore once we use let%Option!\n  let%Option info = user.info;\n  let%Option address = info.address;\n  let%Option street = address.street;\n  Some(street->Js.String.toUpperCase);\n};\n```\n\nThat code is flat, readable, and understandable. Here's an alternative without the syntax sugar:\n\n```reason\nlet getStreet = (maybeUser: option(user)): option(string) => {\n  maybeUser->Belt.Option.flatMap(user =>\n    user.info\n    ->Belt.Option.flatMap(personalInfo =>\n        personalInfo.address\n        ->Belt.Option.flatMap(address =>\n            address.street\n            ->Belt.Option.flatMap(street =>\n                Some(street->Js.String.toUpperCase)\n              )\n          )\n      )\n  );\n};\n```\n\nMuch nicer to have the sugar, no? This PPX really shines, though, when we use it to chain async operations, since that has to be done quite a lot in Javascript, especially server-side, and it typically happens multiple times in the middle of large and complex functions.\n\nHere's a more complex example of an async control flow using the [reason-promise](https://github.com/aantron/promise) library to work with Javascript promises:\n\n```reason\n// reason-promise doesn't ship with native support for this PPX, so we simply\n// add our own by re-defining the module, including all the stuff from the\n// original module, and adding our own function.\nmodule Promise = {\n  include Promise;\n  let let_ = Promise.flatMap;\n\n  // This is totally optional. It can be nice sometimes to return a\n  // non-promise value at the end of a function and have it automatically\n  // wrapped.\n  module Wrap = {\n    let let_ = Promise.map;\n  };\n};\n\nlet logUserIn = (email: string, password: string) => {\n  // Assume this is a function that returns a promise of a hash.\n  let%Promise hash = UserService.hashPassword(password);\n  let%Promise maybeUser = UserService.findUserForEmailAndHash(email, hash);\n  let result =\n    switch (maybeUser) {\n    | Some(user) =>\n      // It even works inside of a switch expression!\n      // Here you can see we're using \".Wrap\" to automatically wrap our result\n      // in a promise.\n      let%Promise.Wrap apiToken = TokenService.generateForUser(user.id);\n      Ok(user.firstName, apiToken);\n    | None =>\n      // We resolve a promise here to match the branch above.\n      Error(\"Sorry, no user found for that email & password combination\")\n      ->Promise.resolved\n    };\n\n  // Since let_ is defined as \"flatMap\" we've got to remember to return a promise\n  // at the end of the function! Remember, all the lines after each let% just get\n  // turned into a callback!\n  Promise.resolved(result);\n};\n```\n\nThere's a whole lot that can be done with this PPX. It's even possible to go a little crazy and start writing modules that combine monads, like `AsyncOption` that will specifically handle optional values inside of promises. But, in practice, those modules are seldom needed. Don't go too crazy, keeping it simple will get you a long, long way.\n\nThings to remember:\n\n- You don't have to name your module anything special. It could be named `Foo` and you can `let%Foo blah = ...`.\n- Simple is better than complex.\n- Obvious is usually better than hidden.\n\n## About Performance\n\nIt's worth noting that this PPX simply produces a _function callback structure_. Why is this important? There are potential performance gains in situations where avoiding a callback structure is possible.\n\nFor example, this handrwitten code, which is pretty much what the PPX produces:\n\n```reason\nlet getStreet = (maybeUser: option(user)): option(string) => {\n  maybeUser->Belt.Option.flatMap(user =>\n    user.info\n    ->Belt.Option.flatMap(personalInfo =>\n        personalInfo.address\n        ->Belt.Option.flatMap(address =>\n            address.street\n            ->Belt.Option.flatMap(street =>\n                Some(street->Js.String.toUpperCase)\n              )\n          )\n      )\n  );\n};\n```\n\nIs _functionally_ equivalent, but inferior in terms of performance, to the following hand-written code:\n\n```reason\nlet getStreetExplicit = (maybeUser: option(user)): option(string) => {\n  switch (maybeUser) {\n  | None => None\n  | Some(user) =>\n    switch (user.info) {\n    | None => None\n    | Some(personalInfo) =>\n      switch (personalInfo.address) {\n      | None => None\n      | Some(address) =>\n        switch (address.street) {\n        | None => None\n        | Some(street) => Some(street->Js.String.toUpperCase)\n        }\n      }\n    }\n  };\n};\n```\n\nBecause we're working with Options, we can `switch` on the values instead of `flatMap`-ing. The generated Javascript of the second approach looks like this:\n\n```javascript\nfunction getStreetExplicit(maybeUser) {\n  if (maybeUser !== undefined) {\n    var match = maybeUser[/* info */ 0];\n    if (match !== undefined) {\n      var match$1 = match[/* address */ 0];\n      if (match$1 !== undefined) {\n        var match$2 = match$1[/* street */ 0];\n        if (match$2 !== undefined) {\n          return match$2.toUpperCase();\n        } else {\n          return;\n        }\n      } else {\n        return;\n      }\n    } else {\n      return;\n    }\n  }\n}\n```\n\nOnly one total function invocation is produced by the compiler in this case instead of one invocation _for every bind_. This is significantly faster to execute and may be worth choosing if this function will be very highly trafficked.\n\nIn summary, this PPX is not designed to produce the most performant code in every case. It's just designed to make callbacks easier to use.\n\n## Notes\n\n**A Note about Native VS Bucklescript**\nThis is specifically designed to be helpful with writing Javascript code through ReasonML and Bucklescript. Native OCaml 4.08 implemented a native monadic sugar syntax. So if you're writing native code, I'd suggest skipping this PPX and waiting until [this PR](https://github.com/facebook/reason/pull/2487) lands in Reason, and then adopting the new syntax.\n\n**A Note about Windows**\nCurrently this project only precomiles binaries for linux and OS X according to the needs of existing maintainers. If you're a Windows user and would like to use this PPX, We'd love a pull-request that moves the project from Travis to Azure Pipelines and builds for all three platforms.\n\n## Credit\n\nThis PPX was created by @jaredly and upgraded to the latest OCaml by @anmonteiro. Murphy Randle has merged Antonio's changes to upgrade the package for Bucklescript 6.x and 7.x, re-packaged it to build with `esy` and precompiled binaries for osx and linux. Murphy has also written this readme to describe the most common use-case for this PPX. More features are available but undocumented in this readme. You can see them here: https://github.com/jaredly/let-anything.\n\n- Lovely readme artwork https://icons8.com/ouch/illustration/eastwood-page-under-construction\n","readmeFilename":"README.md"}