{"action":"created","issue":{"url":"https://api.github.com/repos/ramda/ramda/issues/1408","labels_url":"https://api.github.com/repos/ramda/ramda/issues/1408/labels{/name}","comments_url":"https://api.github.com/repos/ramda/ramda/issues/1408/comments","events_url":"https://api.github.com/repos/ramda/ramda/issues/1408/events","html_url":"https://github.com/ramda/ramda/pull/1408","id":108568959,"number":1408,"title":"implement map and ap for functions","user":{"login":"benperez","id":1057202,"avatar_url":"https://avatars.githubusercontent.com/u/1057202?v=3","gravatar_id":"","url":"https://api.github.com/users/benperez","html_url":"https://github.com/benperez","followers_url":"https://api.github.com/users/benperez/followers","following_url":"https://api.github.com/users/benperez/following{/other_user}","gists_url":"https://api.github.com/users/benperez/gists{/gist_id}","starred_url":"https://api.github.com/users/benperez/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benperez/subscriptions","organizations_url":"https://api.github.com/users/benperez/orgs","repos_url":"https://api.github.com/users/benperez/repos","events_url":"https://api.github.com/users/benperez/events{/privacy}","received_events_url":"https://api.github.com/users/benperez/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":8,"created_at":"2015-09-27T23:58:10Z","updated_at":"2015-09-29T05:51:13Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/ramda/ramda/pulls/1408","html_url":"https://github.com/ramda/ramda/pull/1408","diff_url":"https://github.com/ramda/ramda/pull/1408.diff","patch_url":"https://github.com/ramda/ramda/pull/1408.patch"},"body":"This change was motivated by some [discussion](https://github.com/plaid/sanctuary/issues/102) around porting some higher order Ramda functions into Sanctuary. The common theme among many of these functions is that they are essentially gluing together the results of different functions in different ways. If we implement Functor and Applicative for functions then we get this behavior for free.\r\n```haskell\r\ninstance Functor ((->) r) where\r\n    fmap = (.)\r\n\r\ninstance Applicative ((->) a) where\r\n    pure = const\r\n    (<*>) f g x = f x (g x)\r\n```\r\nThe following simplifications become possible\r\n - `R.both` -> `R.lift(R.and)`\r\n - `R.either` -> `R.lift(R.or)`\r\n - `R.negate` -> `R.lift(R.not)`\r\n - `R.converge` -> `R.lift`\r\n\r\nThere are 2 issues that I ran into\r\n\r\nWhat to do about converging functions that take multiple arguments, for example: \r\n```javascript\r\nconst f = R.converge((a, b) => a + b, a => a, (a, b) => a);\r\nf.length; // 2\r\n```\r\nRight now `ap` just assumes there will only be one argument supplied to a function (but I could easily change it to apply the function to the entire arguments object). `R.converge` takes the max length of all of the functions but it can only do this because it knows the length of both input functions at the same time. If you were to perform the equivalent operation with `R.lift` you'd supply the input functions 1 at a time (although both at once is possible as well):\r\n```javascript\r\nconst g = R.lift((a, b) => a + b); \r\n// g :: (Applicative f, Num c) => f c -> f c -> f c\r\nconst h = g(a => a); // should the function returned have length 1 now? How do you keep track of that? \r\n// h :: (Applicative f, Num c) => f c -> f c\r\nconst i = h((a, b) => a); // should the function returned have length 2 now?\r\n```\r\nI think the simplest solution is just to make `R.both`, `R.either`, etc. just take unary functions as arguments and call it a day.\r\n\r\nThe second problem is around preserving context but it seems like that has already been figured out for converge, I'm just not sure how to add it in to `R.map` and `R.ap`."},"comment":{"url":"https://api.github.com/repos/ramda/ramda/issues/comments/143952217","html_url":"https://github.com/ramda/ramda/pull/1408#issuecomment-143952217","issue_url":"https://api.github.com/repos/ramda/ramda/issues/1408","id":143952217,"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},"created_at":"2015-09-29T05:51:12Z","updated_at":"2015-09-29T05:51:12Z","body":"Great points, @kedashoe.\r\n\r\n> Is wanting a `chain` implementation for functions going too far?\r\n\r\nWe can do that too? Cool! What do you say, @benperez?\r\n"}}