{"action":"created","comment":{"url":"https://api.github.com/repos/scala/slip/pulls/comments/39092301","id":39092301,"diff_hunk":"@@ -0,0 +1,802 @@\n+# SLIP-NNN - Redesigning Collection Views\n+\n+**By: Joshua Suereth and Dmitry Petrashko**\n+\n+This proposal is an attempt to improve the usefulness and consistency of\n+scala's \"view\" API.\n+\n+## History\n+\n+| Date          | Version            |\n+|---------------|--------------------|\n+| Aug 13th 2015 | Initial Draft      |\n+| Sep  8th 2015 | Comment on macros  |  \n+\n+## Introduction to Scala Views\n+\n+Scala's collection library is one of the most widely used in the entire Scala\n+ecosystem, and is a primary tool for a lot of computation.  A large part of the\n+code base revolves around a relatively little used feature: Views.\n+\n+Scala's views are designed to allow users to efficiently chain together a sequence\n+of operations over data, and perform them `lazily` regardless of whether the\n+underlying collection is lazy.  Scala's views are designed to have these aspects:\n+\n+1. Calling `.view.force` on a collection returns something that is functionally\n+    equivalent to the original collection, i.e. preserving as much of the\n+    type as was known before.\n+2. Calling `.view`, executing a sequence of transformations, and then calling\n+    `.force` should return a collection that is functionally equivalent to\n+    just executing the sequence of transformations directly on the original\n+    collection.\n+3. Calling `.view` and a sequence of transformations should not immediately\n+   execute any transformation, but instead defer the operation, e.g. `map`,\n+   `filter`, `slice`\n+4. Calling any operation which must be strictly evaluated will\n+   result in immediate evaluation, e.g. `sum`, `fold`.\n+\n+In addition to these core principles, Scala's current view library also attempts\n+to memoize intermediate values of views.  This means that if we call `map` on\n+a view, and then `take(5)`, the first five `map`ed values will be remembered\n+by the view for any future operations.\n+\n+## Problems with the current design.\n+\n+Let's walk through a few of the problems that exist with the current view\n+design, which has motivated several of us to each try our own hand at alternatives.\n+\n+### Problem 1 - Unwanted Memoization\n+\n+Currently views will sporadically memoize intermediate values.   One of the goals\n+of the library is to consistently defer computation until it's needed.  However,\n+sometimes the intermediate state is not something that needs to be remembered.\n+\n+An example:\n+\n+```scala\n+case class User(..., homeCity: String)\n+val x: Traversable[Record] = DatabaseReader.readTable(\"foo\")\n+def readUser(r: Record): User = ...\n+val pittsburghUserCount =\n+   x.view.map(readUser).filter(_.homeCity == 'Pittsburgh').sum\n+```\n+\n+In this scenario, the view is simply used to chain together two simple collection\n+transformations and one computation.  The view is being used in an attempt to\n+prevent creating intermediate collection values, thereby improving performance\n+of the whole computation.   It is not expected that the view will be shared/used\n+beyond this simple definition.  We expect this to be the primary use case\n+for views.\n+\n+While we also expect persistent views of collections to be a valid use case,\n+we think the library should be tuned for the above use case, while allowing the\n+second.\n+\n+\n+### Problem 2 - Mutable collections\n+\n+\n+Currently, views inconsistently handle mutable collections.  This is, in part,\n+due to memoization and, in part, due to supporting operations which cannot\n+be correctly handled on mutable collections.   Let's look at the first issue:\n+\n+```scala\n+val x = ArrayBuffer(1,2,3,4,5)\n+val y = x.view.take(1)\n+pritnln(x.take(1).mkString(\"\")) // prints: \"1\"\n+pritnln(y.mkString(\"\")) // prints: \"1\"\n+x.clear()\n+println(x.take(1).mkString(\"\")) // prints: \"\"\n+println(y.mkString(\"\")) // throws IndexOutOfBoundsException\n+```\n+\n+This is basically due to the view remembering indicies when the underlying\n+collection is mutable.   However, the current view implementation also\n+requires memoizing the underlying collection for certain operations, here's\n+an example:\n+\n+```scala\n+object foo extends Traversable[Int] {\n+   override def foreach[U](f: Int => U): Unit = {\n+      System.err.println(\"Making stuff\"); f(1); f(2)\n+   }\n+}\n+val y = foo.view.scanLeft(0)(_+_) // Note: this prints `Making stuff`\n+y.head // Note: This will never call the original foreach.\n+```\n+\n+*Note: This issue is actually due to memoizing indicies in the view implementation.*\n+\n+### Problem 3 - Maintenance issues\n+\n+Probably the primary issue with the view lirbary, as it currently exists, is\n+due to maintenance problems.   Views are meant to mimic the collection API\n+as closely as possible.  Each operation of the collection API must be\n+implemented such that it is NOT executed, but instead staged for later\n+execution (if possible).   To do so, currently views directly extend their\n+collection counterparts and override all implementations, e.g.\n+\n+```scala\n+trait TraversableViewLike[+A,\n+                          +Coll,\n+                          +This <: TraversableView[A, Coll] with TraversableViewLike[A, Coll, This]]\n+  extends Traversable[A] with TraversableLike[A, This] with ViewMkString[A]\n+{\n+   ...\n+   override protected[this] def newBuilder: Builder[A, This] =\n+       throw new UnsupportedOperationException(this+\".newBuilder\")\n+}\n+```\n+\n+Here we see a method which is not meant to be used in the view hierarchy must be\n+overridden (with a runtime exception), otherwise the default implementation from\n+our parent would get used.  Because all of Scala's collection libraries provide\n+as much default implementation as possible, this means that ANY new method\n+added to the collection library also involves unimplementing the method\n+in the view library.   This can get particularly hairy as optimizations\n+which get placed into \"lower\" collection classes must then be \"undone\" in the\n+view hierarchy.   Views are constantly in danger of accidentally inheriting\n+an operation which they cannot support.\n+\n+Furthermore, Each new node in the class tree requires extension of the memoized\n+transformations that preceded it.  Here's an instance from `IndexedSeqViewLike`:\n+\n+```scala\n+trait SeqViewLike[+A,\n+                  +Coll,\n+                  +This <: SeqView[A, Coll] with SeqViewLike[A, Coll, This]]\n+  extends Seq[A] with SeqLike[A, This] with IterableView[A, Coll] with IterableViewLike[A, Coll, This]\n+{ self =>\n+  /** Explicit instantiation of the `Transformed` trait to reduce class file size in subclasses. */\n+  private[collection] abstract class AbstractTransformed[+B] extends Seq[B] with super[IterableViewLike].Transformed[B] with Transformed[B]\n+  trait Transformed[+B] extends SeqView[B, Coll] with super.Transformed[B] {\n+    def length: Int\n+    def apply(idx: Int): B\n+    override def toString = viewToString\n+  }  \n+```\n+\n+This `Transformed` trait is one of the tricks used to ensure that views can stage\n+operations and share implementations between each staged result.\n+\n+This results in bugs like [SI-4190](https://issues.scala-lang.org/browse/SI-4190)\n+and [SI-4332](https://issues.scala-lang.org/browse/SI-4332)\n+whereby the parent-behavior of a view is getting used through inheritance, when\n+in fact we'd prefer a compile time error telling us to implement a missing method.\n+\n+```scala\n+val x: ArrayBuffer[String] = ArrayBuffer(\"a\", \"b\", \"c\")\n+val y: IndexedSeq[String] = x.view map (_ + \"0\") // Class cast exception at runtime\n+```\n+\n+\n+## Abstract\n+\n+Here we'd like to propose a new view library with the following goals:\n+\n+* Views should solve the original goals for scala collections, sepcifically:\n+  1. Calling `.view.force` on a collection returns something that is functionally\n+      equivalent to the original collection, i.e. preserving as much of the\n+      type as was known before.\n+  2. Calling `.view`, executing a sequence of transformations, and then calling\n+      `.force` should return a collection that is functionally equivalent to\n+      just executing the sequence of transformations directly on the original\n+      collection.\n+  3. Calling `.view` and a sequence of transformations should not immediately\n+     execute any transformation, but instead defer the operation, e.g. `map`,\n+     `filter`, `slice`\n+  4. Calling any operation which must be strictly evaluated will\n+     result in immediate evaluation, e.g. `sum`, `fold`.\n+* Views should NOT extend the collection API directly, rather they should\n+  have their own interface, whereby implementations can be specifically tuned\n+  for views.\n+* Views should NOT memoize results by default, but instead allow the User to\n+  selectively memoize.\n+* Views are meant to optimise a set of use cases where a chain of\n+  collection-transforming operations is performed, specifically, where a\n+  highly customized `fold` could be used to replace the operation, and where\n+  the standard `Iterator` interface is insufficient or less clear.\n+\n+The new library should take advantage of the lessons learned in Scala's\n+current view library.  They should provide a maximum of flexibility,\n+efficiency without causing maintainability issues.\n+\n+The library will consist of a new `View` class that simple stores:\n+\n+1. an underlying collection\n+2. A transducer stack of staged operations\n+3. A builder it can use to construct a new collection from the staged\n+   operations.\n+\n+\n+### Transducers\n+For those unfamiliar with transducers we'll do a quick introduction here.\n+The word Transducer is used to describe a function which operates against\n+the folding function used when folding a collection.  For example:\n+\n+```scala\n+List(1,2,3).foldLeft(0) { _ + _ }\n+```\n+\n+Here, the foldLeft function takes an accucmulator (starting at 0), and a folding\n+function `{ _ +  _}`.  The folding function is responsible for joining each\n+collection element to the accumulator, returning the next accumulator.\n+\n+Generically, we can treat this folding function as having a type:\n+`(Accumulator, Element) => Accumulator`.\n+\n+Transducers are functions which manipulate these folding functions.  Here is\n+an example interface for a Transducer:\n+\n+```scala\n+type Fold[Accumulator, -Element] = (Accumulator, Element) => Accumulator]\n+trait Transducer[-A, +B] {","path":"text/0000-collection-view-redesign.md","position":233,"original_position":233,"commit_id":"ace690c5c90144e7c43f04e92456b8ad0de5a1a1","original_commit_id":"ace690c5c90144e7c43f04e92456b8ad0de5a1a1","user":{"login":"pedrofurla","id":211820,"avatar_url":"https://avatars.githubusercontent.com/u/211820?v=3","gravatar_id":"","url":"https://api.github.com/users/pedrofurla","html_url":"https://github.com/pedrofurla","followers_url":"https://api.github.com/users/pedrofurla/followers","following_url":"https://api.github.com/users/pedrofurla/following{/other_user}","gists_url":"https://api.github.com/users/pedrofurla/gists{/gist_id}","starred_url":"https://api.github.com/users/pedrofurla/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pedrofurla/subscriptions","organizations_url":"https://api.github.com/users/pedrofurla/orgs","repos_url":"https://api.github.com/users/pedrofurla/repos","events_url":"https://api.github.com/users/pedrofurla/events{/privacy}","received_events_url":"https://api.github.com/users/pedrofurla/received_events","type":"User","site_admin":false},"body":"one too many `]` at the end.","created_at":"2015-09-09T20:32:41Z","updated_at":"2015-09-09T20:32:41Z","html_url":"https://github.com/scala/slip/pull/17#discussion_r39092301","pull_request_url":"https://api.github.com/repos/scala/slip/pulls/17","_links":{"self":{"href":"https://api.github.com/repos/scala/slip/pulls/comments/39092301"},"html":{"href":"https://github.com/scala/slip/pull/17#discussion_r39092301"},"pull_request":{"href":"https://api.github.com/repos/scala/slip/pulls/17"}}},"pull_request":{"url":"https://api.github.com/repos/scala/slip/pulls/17","id":44453644,"html_url":"https://github.com/scala/slip/pull/17","diff_url":"https://github.com/scala/slip/pull/17.diff","patch_url":"https://github.com/scala/slip/pull/17.patch","issue_url":"https://api.github.com/repos/scala/slip/issues/17","number":17,"state":"open","locked":false,"title":"A proposal for deprecating existing views in favor of low-maintenance alternative","user":{"login":"jsuereth","id":29006,"avatar_url":"https://avatars.githubusercontent.com/u/29006?v=3","gravatar_id":"","url":"https://api.github.com/users/jsuereth","html_url":"https://github.com/jsuereth","followers_url":"https://api.github.com/users/jsuereth/followers","following_url":"https://api.github.com/users/jsuereth/following{/other_user}","gists_url":"https://api.github.com/users/jsuereth/gists{/gist_id}","starred_url":"https://api.github.com/users/jsuereth/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jsuereth/subscriptions","organizations_url":"https://api.github.com/users/jsuereth/orgs","repos_url":"https://api.github.com/users/jsuereth/repos","events_url":"https://api.github.com/users/jsuereth/events{/privacy}","received_events_url":"https://api.github.com/users/jsuereth/received_events","type":"User","site_admin":false},"body":"This has already gone through a round of offline reviews. I'll clean up the git history after all commentating is done.","created_at":"2015-09-08T16:29:11Z","updated_at":"2015-09-09T20:32:41Z","closed_at":null,"merged_at":null,"merge_commit_sha":"fc12c7c13c28f9767015992e1e165f644d8ea1b0","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/scala/slip/pulls/17/commits","review_comments_url":"https://api.github.com/repos/scala/slip/pulls/17/comments","review_comment_url":"https://api.github.com/repos/scala/slip/pulls/comments{/number}","comments_url":"https://api.github.com/repos/scala/slip/issues/17/comments","statuses_url":"https://api.github.com/repos/scala/slip/statuses/ace690c5c90144e7c43f04e92456b8ad0de5a1a1","head":{"label":"jsuereth:wip/view-proposal","ref":"wip/view-proposal","sha":"ace690c5c90144e7c43f04e92456b8ad0de5a1a1","user":{"login":"jsuereth","id":29006,"avatar_url":"https://avatars.githubusercontent.com/u/29006?v=3","gravatar_id":"","url":"https://api.github.com/users/jsuereth","html_url":"https://github.com/jsuereth","followers_url":"https://api.github.com/users/jsuereth/followers","following_url":"https://api.github.com/users/jsuereth/following{/other_user}","gists_url":"https://api.github.com/users/jsuereth/gists{/gist_id}","starred_url":"https://api.github.com/users/jsuereth/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jsuereth/subscriptions","organizations_url":"https://api.github.com/users/jsuereth/orgs","repos_url":"https://api.github.com/users/jsuereth/repos","events_url":"https://api.github.com/users/jsuereth/events{/privacy}","received_events_url":"https://api.github.com/users/jsuereth/received_events","type":"User","site_admin":false},"repo":{"id":40676311,"name":"slip","full_name":"jsuereth/slip","owner":{"login":"jsuereth","id":29006,"avatar_url":"https://avatars.githubusercontent.com/u/29006?v=3","gravatar_id":"","url":"https://api.github.com/users/jsuereth","html_url":"https://github.com/jsuereth","followers_url":"https://api.github.com/users/jsuereth/followers","following_url":"https://api.github.com/users/jsuereth/following{/other_user}","gists_url":"https://api.github.com/users/jsuereth/gists{/gist_id}","starred_url":"https://api.github.com/users/jsuereth/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jsuereth/subscriptions","organizations_url":"https://api.github.com/users/jsuereth/orgs","repos_url":"https://api.github.com/users/jsuereth/repos","events_url":"https://api.github.com/users/jsuereth/events{/privacy}","received_events_url":"https://api.github.com/users/jsuereth/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jsuereth/slip","description":"For Scala Library Improvement Process documents and work","fork":true,"url":"https://api.github.com/repos/jsuereth/slip","forks_url":"https://api.github.com/repos/jsuereth/slip/forks","keys_url":"https://api.github.com/repos/jsuereth/slip/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jsuereth/slip/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jsuereth/slip/teams","hooks_url":"https://api.github.com/repos/jsuereth/slip/hooks","issue_events_url":"https://api.github.com/repos/jsuereth/slip/issues/events{/number}","events_url":"https://api.github.com/repos/jsuereth/slip/events","assignees_url":"https://api.github.com/repos/jsuereth/slip/assignees{/user}","branches_url":"https://api.github.com/repos/jsuereth/slip/branches{/branch}","tags_url":"https://api.github.com/repos/jsuereth/slip/tags","blobs_url":"https://api.github.com/repos/jsuereth/slip/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jsuereth/slip/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jsuereth/slip/git/refs{/sha}","trees_url":"https://api.github.com/repos/jsuereth/slip/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jsuereth/slip/statuses/{sha}","languages_url":"https://api.github.com/repos/jsuereth/slip/languages","stargazers_url":"https://api.github.com/repos/jsuereth/slip/stargazers","contributors_url":"https://api.github.com/repos/jsuereth/slip/contributors","subscribers_url":"https://api.github.com/repos/jsuereth/slip/subscribers","subscription_url":"https://api.github.com/repos/jsuereth/slip/subscription","commits_url":"https://api.github.com/repos/jsuereth/slip/commits{/sha}","git_commits_url":"https://api.github.com/repos/jsuereth/slip/git/commits{/sha}","comments_url":"https://api.github.com/repos/jsuereth/slip/comments{/number}","issue_comment_url":"https://api.github.com/repos/jsuereth/slip/issues/comments{/number}","contents_url":"https://api.github.com/repos/jsuereth/slip/contents/{+path}","compare_url":"https://api.github.com/repos/jsuereth/slip/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jsuereth/slip/merges","archive_url":"https://api.github.com/repos/jsuereth/slip/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jsuereth/slip/downloads","issues_url":"https://api.github.com/repos/jsuereth/slip/issues{/number}","pulls_url":"https://api.github.com/repos/jsuereth/slip/pulls{/number}","milestones_url":"https://api.github.com/repos/jsuereth/slip/milestones{/number}","notifications_url":"https://api.github.com/repos/jsuereth/slip/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jsuereth/slip/labels{/name}","releases_url":"https://api.github.com/repos/jsuereth/slip/releases{/id}","created_at":"2015-08-13T18:54:48Z","updated_at":"2015-07-06T19:36:02Z","pushed_at":"2015-09-08T18:38:44Z","git_url":"git://github.com/jsuereth/slip.git","ssh_url":"git@github.com:jsuereth/slip.git","clone_url":"https://github.com/jsuereth/slip.git","svn_url":"https://github.com/jsuereth/slip","homepage":null,"size":106,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":0,"forks":2,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"scala:master","ref":"master","sha":"813824f5b1409e4d200c93bbdd316bd93269d761","user":{"login":"scala","id":57059,"avatar_url":"https://avatars.githubusercontent.com/u/57059?v=3","gravatar_id":"","url":"https://api.github.com/users/scala","html_url":"https://github.com/scala","followers_url":"https://api.github.com/users/scala/followers","following_url":"https://api.github.com/users/scala/following{/other_user}","gists_url":"https://api.github.com/users/scala/gists{/gist_id}","starred_url":"https://api.github.com/users/scala/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scala/subscriptions","organizations_url":"https://api.github.com/users/scala/orgs","repos_url":"https://api.github.com/users/scala/repos","events_url":"https://api.github.com/users/scala/events{/privacy}","received_events_url":"https://api.github.com/users/scala/received_events","type":"Organization","site_admin":false},"repo":{"id":34293483,"name":"slip","full_name":"scala/slip","owner":{"login":"scala","id":57059,"avatar_url":"https://avatars.githubusercontent.com/u/57059?v=3","gravatar_id":"","url":"https://api.github.com/users/scala","html_url":"https://github.com/scala","followers_url":"https://api.github.com/users/scala/followers","following_url":"https://api.github.com/users/scala/following{/other_user}","gists_url":"https://api.github.com/users/scala/gists{/gist_id}","starred_url":"https://api.github.com/users/scala/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scala/subscriptions","organizations_url":"https://api.github.com/users/scala/orgs","repos_url":"https://api.github.com/users/scala/repos","events_url":"https://api.github.com/users/scala/events{/privacy}","received_events_url":"https://api.github.com/users/scala/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/scala/slip","description":"For Scala Library Improvement Process documents and work","fork":false,"url":"https://api.github.com/repos/scala/slip","forks_url":"https://api.github.com/repos/scala/slip/forks","keys_url":"https://api.github.com/repos/scala/slip/keys{/key_id}","collaborators_url":"https://api.github.com/repos/scala/slip/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/scala/slip/teams","hooks_url":"https://api.github.com/repos/scala/slip/hooks","issue_events_url":"https://api.github.com/repos/scala/slip/issues/events{/number}","events_url":"https://api.github.com/repos/scala/slip/events","assignees_url":"https://api.github.com/repos/scala/slip/assignees{/user}","branches_url":"https://api.github.com/repos/scala/slip/branches{/branch}","tags_url":"https://api.github.com/repos/scala/slip/tags","blobs_url":"https://api.github.com/repos/scala/slip/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/scala/slip/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/scala/slip/git/refs{/sha}","trees_url":"https://api.github.com/repos/scala/slip/git/trees{/sha}","statuses_url":"https://api.github.com/repos/scala/slip/statuses/{sha}","languages_url":"https://api.github.com/repos/scala/slip/languages","stargazers_url":"https://api.github.com/repos/scala/slip/stargazers","contributors_url":"https://api.github.com/repos/scala/slip/contributors","subscribers_url":"https://api.github.com/repos/scala/slip/subscribers","subscription_url":"https://api.github.com/repos/scala/slip/subscription","commits_url":"https://api.github.com/repos/scala/slip/commits{/sha}","git_commits_url":"https://api.github.com/repos/scala/slip/git/commits{/sha}","comments_url":"https://api.github.com/repos/scala/slip/comments{/number}","issue_comment_url":"https://api.github.com/repos/scala/slip/issues/comments{/number}","contents_url":"https://api.github.com/repos/scala/slip/contents/{+path}","compare_url":"https://api.github.com/repos/scala/slip/compare/{base}...{head}","merges_url":"https://api.github.com/repos/scala/slip/merges","archive_url":"https://api.github.com/repos/scala/slip/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/scala/slip/downloads","issues_url":"https://api.github.com/repos/scala/slip/issues{/number}","pulls_url":"https://api.github.com/repos/scala/slip/pulls{/number}","milestones_url":"https://api.github.com/repos/scala/slip/milestones{/number}","notifications_url":"https://api.github.com/repos/scala/slip/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/scala/slip/labels{/name}","releases_url":"https://api.github.com/repos/scala/slip/releases{/id}","created_at":"2015-04-21T00:04:00Z","updated_at":"2015-09-09T09:22:34Z","pushed_at":"2015-09-09T02:19:50Z","git_url":"git://github.com/scala/slip.git","ssh_url":"git@github.com:scala/slip.git","clone_url":"https://github.com/scala/slip.git","svn_url":"https://github.com/scala/slip","homepage":null,"size":174,"stargazers_count":20,"watchers_count":20,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":12,"forks":5,"open_issues":12,"watchers":20,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/scala/slip/pulls/17"},"html":{"href":"https://github.com/scala/slip/pull/17"},"issue":{"href":"https://api.github.com/repos/scala/slip/issues/17"},"comments":{"href":"https://api.github.com/repos/scala/slip/issues/17/comments"},"review_comments":{"href":"https://api.github.com/repos/scala/slip/pulls/17/comments"},"review_comment":{"href":"https://api.github.com/repos/scala/slip/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/scala/slip/pulls/17/commits"},"statuses":{"href":"https://api.github.com/repos/scala/slip/statuses/ace690c5c90144e7c43f04e92456b8ad0de5a1a1"}}}}