{"action":"created","comment":{"url":"https://api.github.com/repos/twitter/algebird/pulls/comments/37244246","id":37244246,"diff_hunk":"@@ -0,0 +1,159 @@\n+package com.twitter.algebird.macros\n+\n+import scala.language.experimental.{ macros => sMacros }\n+import scala.reflect.macros.Context\n+import scala.reflect.runtime.universe._\n+\n+/**\n+ * \"Cubes\" a case class or tuple, i.e. for a tuple of type\n+ * (T1, T2, ... , TN) generates all 2^N possible combinations of type\n+ * (Option[T1], Option[T2], ... , Option[TN]).\n+ *\n+ * This is useful for comparing some metric across all possible subsets.\n+ * For example, suppose we have a set of people represented as\n+ * case class Person(gender: String, age: Int, height: Double)\n+ * and we want to know the average height of\n+ *  - people, grouped by gender and age\n+ *  - people, grouped by only gender\n+ *  - people, grouped by only age\n+ *  - all people\n+ *\n+ * Then we could do\n+ * > import com.twitter.algebird.macros.Cuber.cuber\n+ * > val people: List[People]\n+ * > val averageHeights: Map[(Option[String], Option[Int]), Double] =\n+ * >   people.flatMap { p => cuber((p.gender, p.age)).map((_,p)) }\n+ * >     .groupBy(_._1)\n+ * >     .mapValues { xs => val heights = xs.map(_.height); heights.sum / heights.length }\n+ */\n+trait Cuber[I] {\n+  type K\n+  def apply(in: I): TraversableOnce[K]\n+}\n+\n+/**\n+ * For a tuple N produces a result with (N + 1) tuples each of arity N\n+ * such that, for all k from 0 to N, there is a tuple with k Somes\n+ * followed by (N - k) nones.\n+ *\n+ * This is useful for comparing some metric across multiple layers of\n+ * some hierarchy.\n+ * For example, suppose we have some climate data represented as\n+ * case class Data(continent: String, country: String, city: String, temperature: Double)\n+ * and we want to know the average temperatures of\n+ *   - each continent\n+ *   - each (continent, country) pair\n+ *   - each (continent, country, city) triple\n+ *\n+ * Here we desire the (continent, country) and (continent, country, city)\n+ * pair because, for example, if we grouped by city instead of by\n+ * (continent, country, city), we would accidentally combine the results for\n+ * Paris, Texas and Paris, France.\n+ *\n+ * Then we could do\n+ * > import com.twitter.algebird.macros.Roller.roller\n+ * > val data: List[Data]\n+ * > val averageTemps: Map[(Option[String], Option[String], Option[String]), Double] =\n+ * > data.flatMap { d => roller((d.continent, d.country, d.city)).map((_, d)) }\n+ * >   .groupBy(_._1)\n+ * >   .mapValues { xs => val temps = xs.map(_.temperature); temps.sum / temps.length }\n+ */\n+trait Roller[I] {\n+  type K\n+  def apply(in: I): TraversableOnce[K]\n+}\n+\n+object Cuber {\n+  implicit def cuber[T]: Cuber[T] = macro cuberImpl[T]\n+\n+  def cuberImpl[T](c: Context)(implicit T: c.WeakTypeTag[T]): c.Expr[Cuber[T]] = {\n+    import c.universe._\n+\n+    ensureCaseClass(c)\n+\n+    val params = getParams(c)\n+    val arity = params.length\n+    if (arity > 22)\n+      c.abort(c.enclosingPosition, s\"Cannot create Cuber for $T because it has more than 22 parameters.\")\n+    if (arity == 0)\n+      c.abort(c.enclosingPosition, s\"Cannot create Cuber for $T because it has no parameters.\")\n+\n+    val tupleName = {\n+      val tupleType = newTypeName(s\"Tuple${arity}\")\n+      tq\"_root_.scala.$tupleType\"\n+    }\n+    val types = params.map { param => tq\"_root_.scala.Option[${param.returnType}]\" }\n+\n+    val somes = params.zip(Stream.from(1)).map {\n+      case (param, index) =>\n+        val name = newTermName(s\"some$index\")\n+        q\"val $name = _root_.scala.Some(in.$param)\"\n+    }\n+\n+    val options = (1 to arity).map { index =>\n+      val some = newTermName(s\"some$index\")\n+      q\"if (((1 << ${index - 1}) & i) == 0) _root_.scala.None else $some\"","path":"algebird-core/src/main/scala/com/twitter/algebird/macros/Cuber.scala","position":95,"original_position":95,"commit_id":"2de82760df3c8de7132393aa9f762d9abe49f530","original_commit_id":"2de82760df3c8de7132393aa9f762d9abe49f530","user":{"login":"johnynek","id":67958,"avatar_url":"https://avatars.githubusercontent.com/u/67958?v=3","gravatar_id":"","url":"https://api.github.com/users/johnynek","html_url":"https://github.com/johnynek","followers_url":"https://api.github.com/users/johnynek/followers","following_url":"https://api.github.com/users/johnynek/following{/other_user}","gists_url":"https://api.github.com/users/johnynek/gists{/gist_id}","starred_url":"https://api.github.com/users/johnynek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/johnynek/subscriptions","organizations_url":"https://api.github.com/users/johnynek/orgs","repos_url":"https://api.github.com/users/johnynek/repos","events_url":"https://api.github.com/users/johnynek/events{/privacy}","received_events_url":"https://api.github.com/users/johnynek/received_events","type":"User","site_admin":false},"body":"you can evaluate `((1 << ${index - 1}) & i) == 0` at compile time. What not:\r\n\r\n```scala\r\n (1 to arity).map { index =>\r\n  val some = newTermName(s\"some$index\")\r\n  if (((1 << ${index - 1}) & i) == 0) q\"_root_.scala.None\" else q\"$some\"\r\n}\r\n```","created_at":"2015-08-17T22:24:13Z","updated_at":"2015-08-17T22:24:13Z","html_url":"https://github.com/twitter/algebird/pull/483#discussion_r37244246","pull_request_url":"https://api.github.com/repos/twitter/algebird/pulls/483","_links":{"self":{"href":"https://api.github.com/repos/twitter/algebird/pulls/comments/37244246"},"html":{"href":"https://github.com/twitter/algebird/pull/483#discussion_r37244246"},"pull_request":{"href":"https://api.github.com/repos/twitter/algebird/pulls/483"}}},"pull_request":{"url":"https://api.github.com/repos/twitter/algebird/pulls/483","id":42486803,"html_url":"https://github.com/twitter/algebird/pull/483","diff_url":"https://github.com/twitter/algebird/pull/483.diff","patch_url":"https://github.com/twitter/algebird/pull/483.patch","issue_url":"https://api.github.com/repos/twitter/algebird/issues/483","number":483,"state":"open","locked":false,"title":"Cuber/roller macros","user":{"login":"sid-kap","id":6425077,"avatar_url":"https://avatars.githubusercontent.com/u/6425077?v=3","gravatar_id":"","url":"https://api.github.com/users/sid-kap","html_url":"https://github.com/sid-kap","followers_url":"https://api.github.com/users/sid-kap/followers","following_url":"https://api.github.com/users/sid-kap/following{/other_user}","gists_url":"https://api.github.com/users/sid-kap/gists{/gist_id}","starred_url":"https://api.github.com/users/sid-kap/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sid-kap/subscriptions","organizations_url":"https://api.github.com/users/sid-kap/orgs","repos_url":"https://api.github.com/users/sid-kap/repos","events_url":"https://api.github.com/users/sid-kap/events{/privacy}","received_events_url":"https://api.github.com/users/sid-kap/received_events","type":"User","site_admin":false},"body":"Related to https://github.com/twitter/scalding/issues/1420","created_at":"2015-08-14T18:59:22Z","updated_at":"2015-08-17T22:24:13Z","closed_at":null,"merged_at":null,"merge_commit_sha":"572ab95b1577a86509debbcf7b92cec49df24c3d","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/twitter/algebird/pulls/483/commits","review_comments_url":"https://api.github.com/repos/twitter/algebird/pulls/483/comments","review_comment_url":"https://api.github.com/repos/twitter/algebird/pulls/comments{/number}","comments_url":"https://api.github.com/repos/twitter/algebird/issues/483/comments","statuses_url":"https://api.github.com/repos/twitter/algebird/statuses/2de82760df3c8de7132393aa9f762d9abe49f530","head":{"label":"sid-kap:cuber_roller","ref":"cuber_roller","sha":"2de82760df3c8de7132393aa9f762d9abe49f530","user":{"login":"sid-kap","id":6425077,"avatar_url":"https://avatars.githubusercontent.com/u/6425077?v=3","gravatar_id":"","url":"https://api.github.com/users/sid-kap","html_url":"https://github.com/sid-kap","followers_url":"https://api.github.com/users/sid-kap/followers","following_url":"https://api.github.com/users/sid-kap/following{/other_user}","gists_url":"https://api.github.com/users/sid-kap/gists{/gist_id}","starred_url":"https://api.github.com/users/sid-kap/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sid-kap/subscriptions","organizations_url":"https://api.github.com/users/sid-kap/orgs","repos_url":"https://api.github.com/users/sid-kap/repos","events_url":"https://api.github.com/users/sid-kap/events{/privacy}","received_events_url":"https://api.github.com/users/sid-kap/received_events","type":"User","site_admin":false},"repo":{"id":38075853,"name":"algebird","full_name":"sid-kap/algebird","owner":{"login":"sid-kap","id":6425077,"avatar_url":"https://avatars.githubusercontent.com/u/6425077?v=3","gravatar_id":"","url":"https://api.github.com/users/sid-kap","html_url":"https://github.com/sid-kap","followers_url":"https://api.github.com/users/sid-kap/followers","following_url":"https://api.github.com/users/sid-kap/following{/other_user}","gists_url":"https://api.github.com/users/sid-kap/gists{/gist_id}","starred_url":"https://api.github.com/users/sid-kap/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sid-kap/subscriptions","organizations_url":"https://api.github.com/users/sid-kap/orgs","repos_url":"https://api.github.com/users/sid-kap/repos","events_url":"https://api.github.com/users/sid-kap/events{/privacy}","received_events_url":"https://api.github.com/users/sid-kap/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/sid-kap/algebird","description":"Abstract Algebra for Scala","fork":true,"url":"https://api.github.com/repos/sid-kap/algebird","forks_url":"https://api.github.com/repos/sid-kap/algebird/forks","keys_url":"https://api.github.com/repos/sid-kap/algebird/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sid-kap/algebird/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sid-kap/algebird/teams","hooks_url":"https://api.github.com/repos/sid-kap/algebird/hooks","issue_events_url":"https://api.github.com/repos/sid-kap/algebird/issues/events{/number}","events_url":"https://api.github.com/repos/sid-kap/algebird/events","assignees_url":"https://api.github.com/repos/sid-kap/algebird/assignees{/user}","branches_url":"https://api.github.com/repos/sid-kap/algebird/branches{/branch}","tags_url":"https://api.github.com/repos/sid-kap/algebird/tags","blobs_url":"https://api.github.com/repos/sid-kap/algebird/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sid-kap/algebird/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sid-kap/algebird/git/refs{/sha}","trees_url":"https://api.github.com/repos/sid-kap/algebird/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sid-kap/algebird/statuses/{sha}","languages_url":"https://api.github.com/repos/sid-kap/algebird/languages","stargazers_url":"https://api.github.com/repos/sid-kap/algebird/stargazers","contributors_url":"https://api.github.com/repos/sid-kap/algebird/contributors","subscribers_url":"https://api.github.com/repos/sid-kap/algebird/subscribers","subscription_url":"https://api.github.com/repos/sid-kap/algebird/subscription","commits_url":"https://api.github.com/repos/sid-kap/algebird/commits{/sha}","git_commits_url":"https://api.github.com/repos/sid-kap/algebird/git/commits{/sha}","comments_url":"https://api.github.com/repos/sid-kap/algebird/comments{/number}","issue_comment_url":"https://api.github.com/repos/sid-kap/algebird/issues/comments{/number}","contents_url":"https://api.github.com/repos/sid-kap/algebird/contents/{+path}","compare_url":"https://api.github.com/repos/sid-kap/algebird/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sid-kap/algebird/merges","archive_url":"https://api.github.com/repos/sid-kap/algebird/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sid-kap/algebird/downloads","issues_url":"https://api.github.com/repos/sid-kap/algebird/issues{/number}","pulls_url":"https://api.github.com/repos/sid-kap/algebird/pulls{/number}","milestones_url":"https://api.github.com/repos/sid-kap/algebird/milestones{/number}","notifications_url":"https://api.github.com/repos/sid-kap/algebird/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sid-kap/algebird/labels{/name}","releases_url":"https://api.github.com/repos/sid-kap/algebird/releases{/id}","created_at":"2015-06-25T21:42:29Z","updated_at":"2015-06-25T21:42:30Z","pushed_at":"2015-08-17T22:02:22Z","git_url":"git://github.com/sid-kap/algebird.git","ssh_url":"git@github.com:sid-kap/algebird.git","clone_url":"https://github.com/sid-kap/algebird.git","svn_url":"https://github.com/sid-kap/algebird","homepage":"https://twitter.com/scalding","size":4866,"stargazers_count":0,"watchers_count":0,"language":"Scala","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"develop"}},"base":{"label":"twitter:develop","ref":"develop","sha":"57175526cc786890380a76ec5b82c904c4341fad","user":{"login":"twitter","id":50278,"avatar_url":"https://avatars.githubusercontent.com/u/50278?v=3","gravatar_id":"","url":"https://api.github.com/users/twitter","html_url":"https://github.com/twitter","followers_url":"https://api.github.com/users/twitter/followers","following_url":"https://api.github.com/users/twitter/following{/other_user}","gists_url":"https://api.github.com/users/twitter/gists{/gist_id}","starred_url":"https://api.github.com/users/twitter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/twitter/subscriptions","organizations_url":"https://api.github.com/users/twitter/orgs","repos_url":"https://api.github.com/users/twitter/repos","events_url":"https://api.github.com/users/twitter/events{/privacy}","received_events_url":"https://api.github.com/users/twitter/received_events","type":"Organization","site_admin":false},"repo":{"id":5275507,"name":"algebird","full_name":"twitter/algebird","owner":{"login":"twitter","id":50278,"avatar_url":"https://avatars.githubusercontent.com/u/50278?v=3","gravatar_id":"","url":"https://api.github.com/users/twitter","html_url":"https://github.com/twitter","followers_url":"https://api.github.com/users/twitter/followers","following_url":"https://api.github.com/users/twitter/following{/other_user}","gists_url":"https://api.github.com/users/twitter/gists{/gist_id}","starred_url":"https://api.github.com/users/twitter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/twitter/subscriptions","organizations_url":"https://api.github.com/users/twitter/orgs","repos_url":"https://api.github.com/users/twitter/repos","events_url":"https://api.github.com/users/twitter/events{/privacy}","received_events_url":"https://api.github.com/users/twitter/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/twitter/algebird","description":"Abstract Algebra for Scala","fork":false,"url":"https://api.github.com/repos/twitter/algebird","forks_url":"https://api.github.com/repos/twitter/algebird/forks","keys_url":"https://api.github.com/repos/twitter/algebird/keys{/key_id}","collaborators_url":"https://api.github.com/repos/twitter/algebird/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/twitter/algebird/teams","hooks_url":"https://api.github.com/repos/twitter/algebird/hooks","issue_events_url":"https://api.github.com/repos/twitter/algebird/issues/events{/number}","events_url":"https://api.github.com/repos/twitter/algebird/events","assignees_url":"https://api.github.com/repos/twitter/algebird/assignees{/user}","branches_url":"https://api.github.com/repos/twitter/algebird/branches{/branch}","tags_url":"https://api.github.com/repos/twitter/algebird/tags","blobs_url":"https://api.github.com/repos/twitter/algebird/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/twitter/algebird/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/twitter/algebird/git/refs{/sha}","trees_url":"https://api.github.com/repos/twitter/algebird/git/trees{/sha}","statuses_url":"https://api.github.com/repos/twitter/algebird/statuses/{sha}","languages_url":"https://api.github.com/repos/twitter/algebird/languages","stargazers_url":"https://api.github.com/repos/twitter/algebird/stargazers","contributors_url":"https://api.github.com/repos/twitter/algebird/contributors","subscribers_url":"https://api.github.com/repos/twitter/algebird/subscribers","subscription_url":"https://api.github.com/repos/twitter/algebird/subscription","commits_url":"https://api.github.com/repos/twitter/algebird/commits{/sha}","git_commits_url":"https://api.github.com/repos/twitter/algebird/git/commits{/sha}","comments_url":"https://api.github.com/repos/twitter/algebird/comments{/number}","issue_comment_url":"https://api.github.com/repos/twitter/algebird/issues/comments{/number}","contents_url":"https://api.github.com/repos/twitter/algebird/contents/{+path}","compare_url":"https://api.github.com/repos/twitter/algebird/compare/{base}...{head}","merges_url":"https://api.github.com/repos/twitter/algebird/merges","archive_url":"https://api.github.com/repos/twitter/algebird/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/twitter/algebird/downloads","issues_url":"https://api.github.com/repos/twitter/algebird/issues{/number}","pulls_url":"https://api.github.com/repos/twitter/algebird/pulls{/number}","milestones_url":"https://api.github.com/repos/twitter/algebird/milestones{/number}","notifications_url":"https://api.github.com/repos/twitter/algebird/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/twitter/algebird/labels{/name}","releases_url":"https://api.github.com/repos/twitter/algebird/releases{/id}","created_at":"2012-08-02T17:24:42Z","updated_at":"2015-08-15T21:22:29Z","pushed_at":"2015-08-17T22:02:23Z","git_url":"git://github.com/twitter/algebird.git","ssh_url":"git@github.com:twitter/algebird.git","clone_url":"https://github.com/twitter/algebird.git","svn_url":"https://github.com/twitter/algebird","homepage":"https://twitter.com/scalding","size":8290,"stargazers_count":977,"watchers_count":977,"language":"Scala","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":180,"mirror_url":null,"open_issues_count":94,"forks":180,"open_issues":94,"watchers":977,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/twitter/algebird/pulls/483"},"html":{"href":"https://github.com/twitter/algebird/pull/483"},"issue":{"href":"https://api.github.com/repos/twitter/algebird/issues/483"},"comments":{"href":"https://api.github.com/repos/twitter/algebird/issues/483/comments"},"review_comments":{"href":"https://api.github.com/repos/twitter/algebird/pulls/483/comments"},"review_comment":{"href":"https://api.github.com/repos/twitter/algebird/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/twitter/algebird/pulls/483/commits"},"statuses":{"href":"https://api.github.com/repos/twitter/algebird/statuses/2de82760df3c8de7132393aa9f762d9abe49f530"}}}}