{"action":"created","issue":{"url":"https://api.github.com/repos/plaid/sanctuary/issues/53","labels_url":"https://api.github.com/repos/plaid/sanctuary/issues/53/labels{/name}","comments_url":"https://api.github.com/repos/plaid/sanctuary/issues/53/comments","events_url":"https://api.github.com/repos/plaid/sanctuary/issues/53/events","html_url":"https://github.com/plaid/sanctuary/issues/53","id":89070679,"number":53,"title":"function equivalent of \"do\" notation","user":{"login":"davidchambers","id":210406,"avatar_url":"https://avatars.githubusercontent.com/u/210406?v=3","gravatar_id":"","url":"https://api.github.com/users/davidchambers","html_url":"https://github.com/davidchambers","followers_url":"https://api.github.com/users/davidchambers/followers","following_url":"https://api.github.com/users/davidchambers/following{/other_user}","gists_url":"https://api.github.com/users/davidchambers/gists{/gist_id}","starred_url":"https://api.github.com/users/davidchambers/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/davidchambers/subscriptions","organizations_url":"https://api.github.com/users/davidchambers/orgs","repos_url":"https://api.github.com/users/davidchambers/repos","events_url":"https://api.github.com/users/davidchambers/events{/privacy}","received_events_url":"https://api.github.com/users/davidchambers/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":17,"created_at":"2015-06-17T16:58:13Z","updated_at":"2015-06-17T19:19:31Z","closed_at":null,"body":"Let's consider a problem which a hypothetical `do` function might help us to solve:\r\n\r\n> Given an object, return the square root of the number represented by the string at path `a.b.c`. Do not assume, however, that the path `a.b.c` will exist.\r\n\r\nHere's the obvious naive solution:\r\n\r\n```javascript\r\n// naive :: Object -> Number\r\nconst naive = o => Math.sqrt(parseFloat(o.a.b.c));\r\n```\r\n\r\nThis won't handle all the following inputs:\r\n\r\n  - `{a: {b: {c: '2.25'}}}`\r\n  - `{a: {b: {c: 'blah'}}}`\r\n  - `{a: {b: {}}}`\r\n  - `{a: {}}`\r\n  - `{}`\r\n\r\nThis is how I would solve this problem today:\r\n\r\n```javascript\r\n// safe1 :: Object -> Maybe Number\r\nconst safe1 = R.pipe(\r\n  S.get('a'),\r\n  R.chain(S.get('b')),\r\n  R.chain(S.get('c')),\r\n  R.chain(S.parseFloat),\r\n  R.map(Math.sqrt)\r\n);\r\n```\r\n\r\nWith two small changes we can make this more uniform:\r\n\r\n```javascript\r\n// safe2 :: Object -> Maybe Number\r\nconst safe2 = R.pipe(\r\n  S.Just,\r\n  R.chain(S.get('a')),\r\n  R.chain(S.get('b')),\r\n  R.chain(S.get('c')),\r\n  R.chain(S.parseFloat),\r\n  R.chain(R.compose(S.Just, Math.sqrt))\r\n);\r\n```\r\n\r\nIf we then changed the type from `Object -> Maybe Number` to `Maybe Object -> Maybe Number` we could remove the first step of the pipeline:\r\n\r\n```javascript\r\n// safe3 :: Maybe Object -> Maybe Number\r\nconst safe3 = R.pipe(\r\n  R.chain(S.get('a')),\r\n  R.chain(S.get('b')),\r\n  R.chain(S.get('c')),\r\n  R.chain(S.parseFloat),\r\n  R.chain(R.compose(S.Just, Math.sqrt))\r\n);\r\n```\r\n\r\nNow we have a form which can surely be abstracted! Given a list of functions we want to apply `R.chain` to each, then apply `R.pipe` to the list as positional arguments:\r\n\r\n```javascript\r\n// $do :: Monad m => [(a -> m b), (b -> m c), ..., (y -> m z)] -> m a -> m z\r\nconst $do = fs => R.apply(R.pipe, R.map(R.chain, fs));\r\n```\r\n\r\nThis can be written point-free:\r\n\r\n```javascript\r\n// $do :: Monad m => [(a -> m b), (b -> m c), ..., (y -> m z)] -> m a -> m z\r\nconst $do = R.pipe(R.map(R.chain), R.apply(R.pipe));\r\n```\r\n\r\nLet's rewrite `safe3` in terms of `$do`:\r\n\r\n```javascript\r\n// safe4 :: Maybe Object -> Maybe Number\r\nconst safe4 = $do([\r\n  S.get('a'),\r\n  S.get('b'),\r\n  S.get('c'),\r\n  S.parseFloat,\r\n  R.compose(S.Just, Math.sqrt),\r\n]);\r\n```\r\n\r\n`$do` works for any [Monad][1], and should reduce demand for \"composed-chain\" functions such as [`S.gets`][2].\r\n\r\nWhat do you think? Shall we add this? If so, what should it be named? `S.do` is problematic since `do` is not a valid identifier. `S.$do` is pretty ugly. Does `do` go by other names?\r\n\r\n\r\n[1]: https://github.com/fantasyland/fantasy-land#monad\r\n[2]: https://github.com/plaid/sanctuary#gets\r\n"},"comment":{"url":"https://api.github.com/repos/plaid/sanctuary/issues/comments/112918961","html_url":"https://github.com/plaid/sanctuary/issues/53#issuecomment-112918961","issue_url":"https://api.github.com/repos/plaid/sanctuary/issues/53","id":112918961,"user":{"login":"buzzdecafe","id":716260,"avatar_url":"https://avatars.githubusercontent.com/u/716260?v=3","gravatar_id":"","url":"https://api.github.com/users/buzzdecafe","html_url":"https://github.com/buzzdecafe","followers_url":"https://api.github.com/users/buzzdecafe/followers","following_url":"https://api.github.com/users/buzzdecafe/following{/other_user}","gists_url":"https://api.github.com/users/buzzdecafe/gists{/gist_id}","starred_url":"https://api.github.com/users/buzzdecafe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/buzzdecafe/subscriptions","organizations_url":"https://api.github.com/users/buzzdecafe/orgs","repos_url":"https://api.github.com/users/buzzdecafe/repos","events_url":"https://api.github.com/users/buzzdecafe/events{/privacy}","received_events_url":"https://api.github.com/users/buzzdecafe/received_events","type":"User","site_admin":false},"created_at":"2015-06-17T19:19:30Z","updated_at":"2015-06-17T19:19:30Z","body":"> They're all specific cases of a more general Semigroupoid idea.\r\n\r\nI recall you and @scott-christopher discussing that in the gitter room. i may take a whack at unifying our many composers into one.\r\n\r\nWhich raises the thought: A good name for a javascript composition lib would be Bach.js\r\n"}}