{"action":"created","comment":{"url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/comments/46907367","id":46907367,"diff_hunk":"@@ -0,0 +1,375 @@\n+---\n+comments: true\n+date: 2015-12-03 10:00:00\n+layout: post\n+slug: intro-clojure\n+title: Clojure Explained - In the world of functions\n+summary: For many Clojure is not only a new language but a new way of thinking. Let's start exploring some of the concepts that are hard to grasp when you first encounter this language.\n+author: Tomasz Biernacki\n+feed_group: clojure\n+tags:\n+- Clojure\n+- ClojureScript\n+---\n+## <a name=\"why-this-post\" href=\"#why-this-post\">Why this post?</a>\n+\n+Learning a new skill which is not related to anything you already know can be\n+challenging. You have to overcome many obstacles from the very beginning because\n+everything is different. The new ideas and concepts are often confusing and seem\n+counter-intuitive.\n+\n+Consider speaking a new language. If it's in the same language family, as the \n+one you already know, you can use your previous knowledge. If it's not - you \n+have to learn from scratch. If you already know Italian for example, learning a \n+similar language like Spanish will be fairly easy. Even learning German, which \n+belongs to another language branch, wouldn’t be that hard neither. That's \n+because all the mentioned languages belong to the same \n+[Indo-European](https://en.wikipedia.org/wiki/Indo-European_languages)\n+language family, so even if they are clearly different, they also share many \n+common aspects. Sticking with our example, consider now learning Mandarin \n+Chinese (or vice versa): there are less concepts you can relate to, and not \n+only the words and grammar are different but even the writing system and the \n+culture (which influences the language) are totally new.\n+\n+We can use this analogy in the world of programming languages, too: if you \n+already know Python, learning Ruby will be easy. Even switching from Java to \n+Python is not that complicated: you need time to learn the syntax and some \n+technical details but many concepts like variables, classes, loops are \n+basically the same. \n+\n+And here comes Clojure: if you have an imperative, OOP background, like many of \n+us have, the challenge here consists not only in learning a new language but a \n+new way of thinking, a new programming paradigm.\n+\n+If you have searched for Clojure before you could have found that it is a \n+general-purpose, dynamic programming language with the following characteristics:\n+\n+ * it's a functional language\n+ * it's highly interactive\n+ * encourages immutability\n+ * it's a hosted language (JVM, CLR, ClojureScript for the browser)\n+ * it's a dialect of Lisp \n+ * it allows you to write powerful macros\n+ * simplifies multi-threaded programming\n+\n+You can find more information [here](http://clojure.org/features) and \n+[here](http://clojure.org/getting_started).\n+\n+Considering that many programmers are intimidated by the parenthesis alone,\n+I decided to write a series of articles to help you switch your thinking to the \n+Clojure way.\n+\n+Ideally you are already familiar with the basis of the language and you know how\n+to use the REPL. If  this is not the case please read first\n+[this tutorial](http://learnxinyminutes.com/docs/clojure/) and \n+[this one](https://github.com/shaunlebron/ClojureScript-Syntax-in-15-minutes).\n+\n+You can use the standard Clojure REPL to follow along or experiment with \n+[our ClojureScript repl](http://www.clojurescript.io).\n+\n+## <a name=\"functions\" href=\"#functions\">Functions</a>\n+\n+### What we want to do\n+\n+In this first post we'll be writing our own implementation of one of the core \n+Clojure functions: `map`. Along the way, I'll explain what functions and \n+higher-order functions are, how we can use recursion to iterate through a \n+collection, and why we'd want to use lazy sequences. \n+\n+First let's understand what we want to achieve. `map` is a built-in function \n+that takes as arguments another function `f` and a collection `coll`. Then \n+builds a new collection by applying `f` to each element. To clarify, let's see \n+an example: \n+\n+<script src=\"https://gist.github.com/tomasz-biernacki/d3c0270b73dc465db141.js\">\n+</script>\n+\n+Here `map` takes the elements `1 2 3`, increments each of them (`inc` function) \n+and returns a new collection with the incremented values `2 3 4`.\n+\n+So let's build step by step our own implementation of `map`.\n+\n+### Functions and higher-order functions\n+\n+In OOP, the building blocks of an application are classes: when we need to solve\n+a problem we usually think of creating a new class, adding some fields and \n+methods, then maybe create a bunch of other classes and connect them together.\n+\n+In Clojure on the other hand, and FP in general, functions do the heavy lifting.\n+You pass them data as input. They give you the result as output. This output can\n+be passed as input for another function and so on until you find what you were \n+looking for.\n+\n+Of course this is a simplification, but it's also the first step to \"get\" \n+Clojure.\n+\n+Let's define our first function. In Clojure we do that with the `defn` macro.\n+\n+<script src=\"https://gist.github.com/tomasz-biernacki/c56bda08a7b40c16b4a2.js\">\n+</script>\n+\n+The function is called `compute-number` (line `1`) and has one parameter\n+`n` (inside square brackets on line `2`). The return value of the function is the\n+last evaluated expression and in our case is the incremented number (line `3`).\n+You can see that by calling `compute-number` with *10* as argument we get back \n+*11* (lines `5-6`).\n+\n+Functions are so important and central that they are treated like any other \n+value: for example we can pass them as arguments to another function. Let's try\n+to do that in `compute-number`:\n+\n+<script src=\"https://gist.github.com/tomasz-biernacki/29d67dbf8f5dbaf15833.js\">\n+</script>\n+\n+On line `2` we added a parameter `f` inside square brackets and we expect it to\n+be a function. Then on line `3` we call `f` with `n` as argument. Line `2` and \n+`3` are similar but the former is the parameter list and the latter is the actual\n+function call. The return value of `compute-number` will depend on what function\n+we pass to it and you can see that in the examples on lines `5`, `8` and `11`.\n+The first time we increment `n` (passing the built-in function `inc`), than we \n+decrement it (`dec`) and in the last example we ask if *10* is even, which of \n+course is true.\n+\n+Passing functions as arguments is common practice and this is exactly what is \n+happening with the `map` function we saw earlier. Let's see another similar \n+example, `filter`:\n+\n+<script src=\"https://gist.github.com/tomasz-biernacki/973e1231150b49aa8ba0.js\">\n+</script>\n+\n+`filter` returns the items in a collection for which the function passed as first \n+parameter returns true, in this case `odd?`, and we can see that the new collection\n+consists only of odd numbers.\n+\n+Functions like `map` and `filter` (and also `compute-number`) are called \n+[higher-order functions](https://en.wikipedia.org/wiki/Higher-order_function), \n+because they take another one as argument.\n+\n+We are ready now to start writing our own implementation of `map`:\n+\n+<script src=\"https://gist.github.com/tomasz-biernacki/d2fa1c069f1a4dd03d7f.js\">\n+</script>\n+\n+The function will be called `my-map` (line `1`) and will take a function `f` and a \n+collection `coll` as parameters (line `2`). Since we don't know yet how to \n+iterate through a collection let's limit ourself to applying `f` to the first \n+element (line `3`): we can see in the examples on line `5` and `8` that we were\n+able to call the function correctly, the first time incrementing the first element\n+and the second time decrementing it. \n+\n+Now let's see how we can apply `f` to each element.\n+\n+## <a name=\"recursion\" href=\"#recursion\">Recursion</a>\n+\n+To iterate through a collection in languages like Java or C the idiomatic way is\n+to use a `for` loop. Clojure has no for loop as we know it from imperative \n+languages and no direct mutable variables.\n+We need to use recursion instead. You probably already know that a recursive\n+function is a function that calls itself directly or indirectly. \n+But what does it mean? How can we apply recursion to iterate through a collection?\n+\n+First we need a way to isolate one element at a time: we already did this\n+with the first element, but we need to isolate the second, the third and so on.\n+It turns out that Clojure provides another useful function, `rest`, to retrieve\n+all the elements after the first. \n+\n+So let's consider as example the list `'(1 2 3 4)` and let's try to use `first`\n+and `rest` to manually iterate through each element: \n+\n+1. at the beginning, `first` will return `1` and `rest` will return a new \n+collection `'(2 3 4)`.\n+2. with this new collection, `first` will be `2` and the `rest` will be `'(3 4)`\n+3. `first` will be `3` and `rest` will be `'(4)`\n+4. `first` will be `4` and `rest`, since there are no more elements, just an \n+empty list, `()`\n+5. the empty list is passed: `first` will be `nil`...and `rest`? `rest` will be \n+again an empty list.\n+\n+Sooner or later we need to stop this process, and in our case it's when the list \n+is empty. We can see that what we did is recursive by nature: we are using `first`\n+and `rest` again and again, but each time with a different input. We are taking\n+the first element from a collection, doing something with it, and repeating the \n+same process again for the rest of the collection.\n+\n+<script src=\"https://gist.github.com/tomasz-biernacki/2a0dc6d5baa994336421.js\"></script>\n+\n+A recursive function is tricky because many things are happening in the same \n+place: if the collection is not empty (line `3`) go ahead, do something with the\n+first element (e.g. apply a function to it, line `4`) and than process the rest\n+of the collection recursively, using the same `my-map` function (line `5`). If \n+the collection is empty, return it (line `6`) and end the recursive calls. Note \n+that `(seq coll)` is just an \n+[idiomatic way](https://clojuredocs.org/clojure.core/empty_q) to check if the\n+collection is empty.\n+Also note that we need to wrap lines `4` and `5` with the special form `do`. \n+We'll get rid of it in the next step. For now let's test our function so far:\n+\n+<script src=\"https://gist.github.com/tomasz-biernacki/24e9e0eb2c1ed35e0d2e.js\">\n+</script>\n+\n+On line `1` we use the `println` function for test purpose and we can see that \n+we are getting each element one at a time. We also cover the edge case of an \n+empty collection (line `8`).\n+\n+At this point we know how to pass a function to another one and how to apply it \n+to each element but you see in line `11` that we are not getting back a new \n+collection. We are applying `f` to a single element but we are not using the new \n+value, we are just incrementing an element and then throwing it away.\n+\n+## <a name=\"build-new-collection\" href=\"#build-new-collection\">Building a new collection</a>\n+\n+We need a way to build a new collection, and that's what the `cons` function is\n+for: given an element and a collection, it returns a new collection with that\n+element added to the front. Another useful and often used function is\n+[conj](https://clojuredocs.org/clojure.core/conj) but \n+[for our last example](#lazy-sequences) we'll stick with \n+[cons](http://stackoverflow.com/questions/12389303/clojure-cons-vs-conj-with-lazy-seq) .\n+\n+<script src=\"https://gist.github.com/tomasz-biernacki/6fb5f0c18155a36b270e.js\">\n+</script>\n+\n+You can note the special cases of *consing* an element to an empty list and a \n+`nil` value, they will be useful soon.\n+\n+This is also the trickiest part because since `cons` adds an element to the \n+front we need to build our new collection from the last element, and *cons* the \n+previous ones backwards.\n+\n+Consider how we could build `'(11 21 31)` out of `(my-map inc '(10 20 30))`:\n+\n+1. `'(11 21 31)` is `11` added to the front of `'(21 31)`\n+2. `'(21 31)` is `21` added to the front of `'(31)`\n+3. `'(31)` is `31` added to the front of an emtpy list or `nil`\n+\n+So what we need to do is `cons` each element to the rest of the collection, \n+which will be calculated recursively. This is how our function calls work at \n+this moment:\n+\n+~~~\n+(1) (my-map inc '(10 20 30))      =\n+(2)        (inc 10)\n+(3)        (map inc '(20 30))     = \n+(4)             (inc 20)\n+(5)             (map inc '(30))   =\n+(6)                  (inc 30)\n+(7)                  (map inc ()) = ()\n+~~~\n+\n+We need (5) to be the result of *consing* (6) with (7), that is \n+`(cons (inc 30) ())` which gives `'(31)`.\n+\n+Than (3) is the result of *consing* (4) and (5), that is \n+`(cons (inc 20) '(31))` which gives `'(21 31)`.\n+\n+Eventually, (1) is the result of *consing* (2) and (3), that is \n+`(cons (inc 10) '(21 31))` which gives the expected result of `'(11 21 31)`. \n+\n+Our new version of `my-map` looks like this (note that since `cons` on line `4`\n+is just one expression we are able to remove the `do` special form):\n+\n+<script src=\"https://gist.github.com/tomasz-biernacki/5cf1be3dd90f439c1c38.js\">\n+</script>\n+\n+It's like starting from one point and by recursive calls going forward\n+\n+~~~\n+ * -------> * -------> * ---------> *\n+ 11         21         31           ()\n+                                    |\n+                        '(31) <-----+\n+                         |\n+             '(21 31) <--+\n+              |\n+'(11 21 31) <-+\n+~~~\n+\n+and than going back and *consing* the elements.\n+\n+We have an additional bonus here: in Clojure `first`, `rest` and `cons` are \n+generic functions that work with any built-in data structure, and since we are\n+using only those functions inside `my-map`, we can pass any collection as \n+argument. Before we dive into lazy sequences let's see some examples:\n+\n+<script src=\"https://gist.github.com/tomasz-biernacki/b2afe9a120849ada6c40.js\">\n+</script>\n+\n+I won't cover this topic in this post but it's interesing to note that our \n+function works perfectly fine with vectors, lists, sets and any other data \n+structure that obeys to the `first/rest/cons` contract. You can read more about\n+programming to abstractions\n+[here](http://www.braveclojure.com/core-functions-in-depth/#Programming_to_Abstractions).\n+\n+## <a name=\"lazy-sequences\" href=\"#lazy-sequences\">Lazy sequences</a>\n+\n+There is only one more problem with our implementation: imagine that we have a \n+vector which contains the annual average temperatures for a given city. We'll \n+take as example the beautiful city of \n+[Gdansk](http://www.holiday-weather.com/gdansk/averages/),\n+where [our office is located](https://www.scalac.io):\n+`[-2 -2 1 6 11 14 17 17 12 8 2 -2]`.\n+\n+We would like to convert these temperatures from Celsius to Fahrenheit, and by\n+now it works well:\n+\n+<script src=\"https://gist.github.com/tomasz-biernacki/791c9e220d237d0c24ae.js\">\n+</script>\n+\n+But what if we needed only the first 3 elements? Maybe we are planning a holiday \n+in winter :)\n+\n+<script src=\"https://gist.github.com/tomasz-biernacki/171ec6d3e3c914f3427a.js\">\n+</script>\n+\n+It's still working but the problem is that even if need only the first 3 \n+elements, `my-map` will compute the new values for the entire collection, and we \n+don't want that.\n+\n+Clojure helps us with the idea of lazy sequences: these are sequences whose \n+members are evaluated only when needed. A lazy sequence can be though of as a \n+recipe for retrieving the next elements, which will be computed only if we ask \n+for them. Another common way of saying that the elements are computed it's to \n+say that they are realized. In our example we need only three elements, so only \n+three will be realized. It's also very simple to create a lazy sequence: all we \n+need to do is wrap our function body with the `lazy-seq` macro.\n+\n+<script src=\"https://gist.github.com/tomasz-biernacki/b37e7982cd5516b78f2e.js\">\n+</script>\n+\n+You can note that the output on line `10` is the same as before, but this time \n+only three elements were actually realized. To verify that you can add a `println` \n+inside the function and compare the version with `lazy-seq` and the one without.\n+\n+<script src=\"https://gist.github.com/tomasz-biernacki/7ae3c00e0a42baa15291.js\">\n+</script>\n+\n+This of course is a trivial example but it's easy to imagine the benefit of lazy \n+sequences when we are dealing with *larger* collections. Keep also in mind that \n+many Clojure built-in functions that produce lazy sequences don't realize one \n+element at a time like in our example. Elements are rather realized ahead of \n+time in chunks (32 elements at a time). This is done for performance reasons \n+and it won't cover it here. \n+You can read more [here](http://clojure-doc.org/articles/language/laziness.html).\n+\n+### Final note\n+\n+In our own implementation we have the *else* branch because when an empty \n+collection is passed as argument we want to return it back instead of `nil`. But \n+with lazy sequences we don't need to do that because `(lazy-seq nil)` is equal \n+to `()`, so we can change the `if` to `when` and get rid of one line. \n+\n+<script src=\"https://gist.github.com/tomasz-biernacki/cb11481365077cbd403f.js\">\n+</script>\n+\n+All the examples above will work and will be our last version.","path":"_posts/2015-12-03-clojure-explained-recursion.md","position":363,"original_position":363,"commit_id":"8e68c7cc37c4d49ec8621e890a3a8a1afa08e0b3","original_commit_id":"8e68c7cc37c4d49ec8621e890a3a8a1afa08e0b3","user":{"login":"arichiardi","id":1724788,"avatar_url":"https://avatars.githubusercontent.com/u/1724788?v=3","gravatar_id":"","url":"https://api.github.com/users/arichiardi","html_url":"https://github.com/arichiardi","followers_url":"https://api.github.com/users/arichiardi/followers","following_url":"https://api.github.com/users/arichiardi/following{/other_user}","gists_url":"https://api.github.com/users/arichiardi/gists{/gist_id}","starred_url":"https://api.github.com/users/arichiardi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/arichiardi/subscriptions","organizations_url":"https://api.github.com/users/arichiardi/orgs","repos_url":"https://api.github.com/users/arichiardi/repos","events_url":"https://api.github.com/users/arichiardi/events{/privacy}","received_events_url":"https://api.github.com/users/arichiardi/received_events","type":"User","site_admin":false},"body":"...and the above will be our...","created_at":"2015-12-08T02:07:49Z","updated_at":"2015-12-08T02:07:49Z","html_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/90#discussion_r46907367","pull_request_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/90","_links":{"self":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/comments/46907367"},"html":{"href":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/90#discussion_r46907367"},"pull_request":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/90"}}},"pull_request":{"url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/90","id":52687653,"html_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/90","diff_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/90.diff","patch_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/90.patch","issue_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/90","number":90,"state":"open","locked":false,"title":"Clojure Explained - Recursion","user":{"login":"tomasz-biernacki","id":15321488,"avatar_url":"https://avatars.githubusercontent.com/u/15321488?v=3","gravatar_id":"","url":"https://api.github.com/users/tomasz-biernacki","html_url":"https://github.com/tomasz-biernacki","followers_url":"https://api.github.com/users/tomasz-biernacki/followers","following_url":"https://api.github.com/users/tomasz-biernacki/following{/other_user}","gists_url":"https://api.github.com/users/tomasz-biernacki/gists{/gist_id}","starred_url":"https://api.github.com/users/tomasz-biernacki/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tomasz-biernacki/subscriptions","organizations_url":"https://api.github.com/users/tomasz-biernacki/orgs","repos_url":"https://api.github.com/users/tomasz-biernacki/repos","events_url":"https://api.github.com/users/tomasz-biernacki/events{/privacy}","received_events_url":"https://api.github.com/users/tomasz-biernacki/received_events","type":"User","site_admin":false},"body":"An article about some basic concepts in Clojure: functions, higher-order functions, recursion and lazy sequences.","created_at":"2015-12-04T16:09:30Z","updated_at":"2015-12-08T02:07:49Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f2f51995dee3a51ed4a9caf5bcf0777834f747a9","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/90/commits","review_comments_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/90/comments","review_comment_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/90/comments","statuses_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/statuses/8e68c7cc37c4d49ec8621e890a3a8a1afa08e0b3","head":{"label":"ScalaConsultants:clojure-explained-1","ref":"clojure-explained-1","sha":"8e68c7cc37c4d49ec8621e890a3a8a1afa08e0b3","user":{"login":"ScalaConsultants","id":6207831,"avatar_url":"https://avatars.githubusercontent.com/u/6207831?v=3","gravatar_id":"","url":"https://api.github.com/users/ScalaConsultants","html_url":"https://github.com/ScalaConsultants","followers_url":"https://api.github.com/users/ScalaConsultants/followers","following_url":"https://api.github.com/users/ScalaConsultants/following{/other_user}","gists_url":"https://api.github.com/users/ScalaConsultants/gists{/gist_id}","starred_url":"https://api.github.com/users/ScalaConsultants/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScalaConsultants/subscriptions","organizations_url":"https://api.github.com/users/ScalaConsultants/orgs","repos_url":"https://api.github.com/users/ScalaConsultants/repos","events_url":"https://api.github.com/users/ScalaConsultants/events{/privacy}","received_events_url":"https://api.github.com/users/ScalaConsultants/received_events","type":"Organization","site_admin":false},"repo":{"id":15366666,"name":"ScalaConsultants.github.io","full_name":"ScalaConsultants/ScalaConsultants.github.io","owner":{"login":"ScalaConsultants","id":6207831,"avatar_url":"https://avatars.githubusercontent.com/u/6207831?v=3","gravatar_id":"","url":"https://api.github.com/users/ScalaConsultants","html_url":"https://github.com/ScalaConsultants","followers_url":"https://api.github.com/users/ScalaConsultants/followers","following_url":"https://api.github.com/users/ScalaConsultants/following{/other_user}","gists_url":"https://api.github.com/users/ScalaConsultants/gists{/gist_id}","starred_url":"https://api.github.com/users/ScalaConsultants/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScalaConsultants/subscriptions","organizations_url":"https://api.github.com/users/ScalaConsultants/orgs","repos_url":"https://api.github.com/users/ScalaConsultants/repos","events_url":"https://api.github.com/users/ScalaConsultants/events{/privacy}","received_events_url":"https://api.github.com/users/ScalaConsultants/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io","description":"Scalac blog","fork":false,"url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io","forks_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/forks","keys_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/teams","hooks_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/hooks","issue_events_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/events{/number}","events_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/events","assignees_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/assignees{/user}","branches_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/branches{/branch}","tags_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/tags","blobs_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/refs{/sha}","trees_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/statuses/{sha}","languages_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/languages","stargazers_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/stargazers","contributors_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/contributors","subscribers_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/subscribers","subscription_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/subscription","commits_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/commits{/sha}","git_commits_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/commits{/sha}","comments_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/comments{/number}","issue_comment_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/comments{/number}","contents_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/contents/{+path}","compare_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/merges","archive_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/downloads","issues_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues{/number}","pulls_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls{/number}","milestones_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/milestones{/number}","notifications_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/labels{/name}","releases_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/releases{/id}","created_at":"2013-12-21T22:28:57Z","updated_at":"2015-11-16T06:24:44Z","pushed_at":"2015-12-08T01:23:33Z","git_url":"git://github.com/ScalaConsultants/ScalaConsultants.github.io.git","ssh_url":"git@github.com:ScalaConsultants/ScalaConsultants.github.io.git","clone_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io.git","svn_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io","homepage":null,"size":5362,"stargazers_count":3,"watchers_count":3,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":9,"mirror_url":null,"open_issues_count":2,"forks":9,"open_issues":2,"watchers":3,"default_branch":"master"}},"base":{"label":"ScalaConsultants:master","ref":"master","sha":"195672867fd58ccf55691e92a7409484acda4a8f","user":{"login":"ScalaConsultants","id":6207831,"avatar_url":"https://avatars.githubusercontent.com/u/6207831?v=3","gravatar_id":"","url":"https://api.github.com/users/ScalaConsultants","html_url":"https://github.com/ScalaConsultants","followers_url":"https://api.github.com/users/ScalaConsultants/followers","following_url":"https://api.github.com/users/ScalaConsultants/following{/other_user}","gists_url":"https://api.github.com/users/ScalaConsultants/gists{/gist_id}","starred_url":"https://api.github.com/users/ScalaConsultants/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScalaConsultants/subscriptions","organizations_url":"https://api.github.com/users/ScalaConsultants/orgs","repos_url":"https://api.github.com/users/ScalaConsultants/repos","events_url":"https://api.github.com/users/ScalaConsultants/events{/privacy}","received_events_url":"https://api.github.com/users/ScalaConsultants/received_events","type":"Organization","site_admin":false},"repo":{"id":15366666,"name":"ScalaConsultants.github.io","full_name":"ScalaConsultants/ScalaConsultants.github.io","owner":{"login":"ScalaConsultants","id":6207831,"avatar_url":"https://avatars.githubusercontent.com/u/6207831?v=3","gravatar_id":"","url":"https://api.github.com/users/ScalaConsultants","html_url":"https://github.com/ScalaConsultants","followers_url":"https://api.github.com/users/ScalaConsultants/followers","following_url":"https://api.github.com/users/ScalaConsultants/following{/other_user}","gists_url":"https://api.github.com/users/ScalaConsultants/gists{/gist_id}","starred_url":"https://api.github.com/users/ScalaConsultants/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScalaConsultants/subscriptions","organizations_url":"https://api.github.com/users/ScalaConsultants/orgs","repos_url":"https://api.github.com/users/ScalaConsultants/repos","events_url":"https://api.github.com/users/ScalaConsultants/events{/privacy}","received_events_url":"https://api.github.com/users/ScalaConsultants/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io","description":"Scalac blog","fork":false,"url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io","forks_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/forks","keys_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/teams","hooks_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/hooks","issue_events_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/events{/number}","events_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/events","assignees_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/assignees{/user}","branches_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/branches{/branch}","tags_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/tags","blobs_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/refs{/sha}","trees_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/statuses/{sha}","languages_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/languages","stargazers_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/stargazers","contributors_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/contributors","subscribers_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/subscribers","subscription_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/subscription","commits_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/commits{/sha}","git_commits_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/commits{/sha}","comments_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/comments{/number}","issue_comment_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/comments{/number}","contents_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/contents/{+path}","compare_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/merges","archive_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/downloads","issues_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues{/number}","pulls_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls{/number}","milestones_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/milestones{/number}","notifications_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/labels{/name}","releases_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/releases{/id}","created_at":"2013-12-21T22:28:57Z","updated_at":"2015-11-16T06:24:44Z","pushed_at":"2015-12-08T01:23:33Z","git_url":"git://github.com/ScalaConsultants/ScalaConsultants.github.io.git","ssh_url":"git@github.com:ScalaConsultants/ScalaConsultants.github.io.git","clone_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io.git","svn_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io","homepage":null,"size":5362,"stargazers_count":3,"watchers_count":3,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":9,"mirror_url":null,"open_issues_count":2,"forks":9,"open_issues":2,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/90"},"html":{"href":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/90"},"issue":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/90"},"comments":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/90/comments"},"review_comments":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/90/comments"},"review_comment":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/90/commits"},"statuses":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/statuses/8e68c7cc37c4d49ec8621e890a3a8a1afa08e0b3"}}}}