{"action":"created","comment":{"url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/pulls/comments/201997975","pull_request_review_id":136605403,"id":201997975,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIwMTk5Nzk3NQ==","diff_hunk":"@@ -0,0 +1,664 @@\n+---\n+title: Why is servant a type-level DSL?\n+author: Alp Mestanogullari\n+date: 2018-07-12 20:00\n+toc: true\n+---\n+\n+---\n+\n+This post is an attempt at explaining servant's design as an embedded domain\n+specific language, and particularly why it _had to_ be a _type-level_ domain\n+specific language, given our requirements. Along the way, we will discuss\n+approaches for designing extensible EDSLs in Haskell and see why other simpler\n+approaches just can't meet the said requirements.\n+\n+# It all started with a problem\n+\n+Back in 2014, Sönke Hahn, Julian Arni and myself were working together in \"the\n+Haskell team\" at Zalora on all sorts of projects. Many of them involved serving\n+web applications, querying external APIs or our own services from Haskell,\n+PHP, JS and probably a few other languages. At the time, we were using\n+a few of the well established \"web frameworks\", among which `scotty`,\n+whenever we had to offer some service over HTTP.\n+\n+However, writing all those functions for hitting our own webservices was a lot\n+of manual, error-prone, tedious work. The bigger web applications got, the more\n+tedious it became. And it had to be done once per language in which we wanted to\n+hit the application. This could not continue.\n+\n+For reference, this is what a simple scotty application looks like:\n+\n+``` haskell\n+{-# LANGUAGE OverloadedStrings #-}\n+import Data.Text (split)\n+import Web.Scotty\n+\n+main :: IO ()\n+main = scotty 8000 $\n+  get \"/repeat/:n\" $ do\n+    n <- param \"n\"\n+    json (replicate n n)\n+\n+  post \"/message\" $ do\n+    msg <- jsonData\n+    json (split \"\\n\" msg)\n+```\n+\n+How could we somewhat automate the creation of one client function per\n+endpoint of the web application? In an ideal world, we would just show this\n+application to some program or library and it would collect all the data it\n+needs about the overall structure of the application from the code itself, in\n+order to produce 2 client functions:\n+\n+``` haskell\n+-- Client for the first endpoint.\n+--\n+-- The Int is the value you want to set \":n\" to (/repeat/23, /repeat/10, ...).\n+getRepeat :: Int -> ClientMonad [Int]\n+\n+-- Client for the second endpoint.\n+--\n+-- The JSON body (just a naked string) to send is the Text argument.\n+postMessage :: Text -> ClientMonad [Text]\n+```\n+\n+which would do all the hard work of preparing an HTTP request for us, even\n+taking care of JSON encoding and decoding for us. But... the entire structure\n+of the application is just hidden in the `do` block and we just cannot\n+programmatically access it.\n+\n+So... this is not realistically doable. We clearly _need_ to change (a little bit?\n+a lot?) the way we write our applications, making sure we get a description\n+of the web's application structure (the endpoints, the part of the request they\n+use or depend on, what they return) that we could then hand over to _something_,\n+which would get us our client functions.\n+\n+We will now try implementating such a web application description DSL in the\n+most straightforward way possible.\n+\n+_Note:_ We could define a DSL with an interpreter that spits out Haskell code\n+for us (through Template Haskell or another mechanism). The result would be\n+typed, but the _process_ would not be. The translation from the DSL to the\n+result would be untyped code, therefore easy to get wrong. As Haskell\n+programmers, we prefer static type checking where possible. This therefore\n+excludes Template Haskell and other code generation mechanisms.\n+\n+# A first, non-modular attempt\n+\n+We want to produce client functions that look like the ones above, that\n+prepare and send HTTP requests for us by taking some pieces of data\n+given as arguments to those functions and encoding then storing\n+them in the right places of the request (request path for\n+URL captures, request body, headers, etc). Let's perhaps start\n+simple with a data type for describing an endpoint that can be\n+served under some path (which can contain static string fragments\n+and captures), for a given http method, ignoring everything else for now.\n+\n+It could look like this:\n+\n+``` haskell\n+data Method = Get | Post\n+\n+data Endpoint = Static String Endpoint\n+              | Capture Endpoint\n+              | Verb Method\n+\n+-- GET /hello/:name\n+getHello :: Endpoint\n+getHello = Static \"hello\" (Capture (Verb Get))\n+```\n+\n+and, if we want it to look a little more \"servant-y\", we can define:\n+\n+``` haskell\n+infixr 5 :>\n+(:>) :: (Endpoint -> Endpoint) -> Endpoint -> Endpoint\n+f :> x = f x\n+\n+getHelloNew :: Endpoint\n+getHelloNew = Static \"hello\" :> Capture :> Verb Get\n+```\n+\n+Unlike servant though, as you can see with the type of `getHello` and\n+`getHelloNew`, our descriptions are good old Haskell values, both of the\n+`Endpoint` type.\n+\n+Given those few definitions, how could we go about, say, generating links\n+to endpoints? Well, here is a straightforward attempt.\n+\n+``` haskell\n+-- a link here is just a list of path components\n+-- (we ignore query parameters in this post)\n+type Link = [String]\n+\n+linkTo :: Endpoint -> Link\n+linkTo (Static str rest) = str : linkTo rest\n+linkTo (Verb _method)    = []\n+linkTo (Capture rest)    = ??? : linkTo rest\n+```\n+\n+But... what should we put in place of those `???`, if anything?\n+\n+Well, we definitely want to add _some_ path component, to fill the\n+`Capture` slot. However, by definition, a captured path fragment is\n+not fixed, it is allowed to vary. In other words, `Capture :> Verb Post`\n+matches both `POST /x` and `POST /y`. We cannot just pick one value\n+and hope that it is the one the user wanted. We need to take it as an\n+argument. But what about `Capture :> Capture :> Verb Post`? We would need\n+our `linkTo` function to take 2 arguments for that case. And zero additional\n+argument for `Static \"hello\" :> Verb Post`. This is quite problematic.\n+\n+Indeed, we would like the type of `linkTo` to be `Endpoint -> Link`,\n+`Endpoint -> String -> Link`, `Endpoint -> String -> String -> Link` and\n+so on depending on what the `Endpoint` argument is. In other words,\n+we want the return type of `linkTo` (when really seen as a function of\n+one argument, which it is anyway) to depend on the value of type `Endpoint`\n+it gets as input. That is, we want a type that depends on a value, i.e\n+dependent types.\n+\n+The alternative would be:\n+\n+``` haskell\n+-- linkTo assumes that the capture values are given in the same order as we want\n+-- them to appear in the url.\n+linkTo :: Endpoint -> [String] -> Link\n+linkTo (Static str rest) captureValues  = str : linkTo rest captureValues\n+-- when there is at least one value left in the list, we use it.\n+-- when there isn't... we error out.\n+linkTo (Capture rest)    (c : cs)       = c   : linkTo rest cs\n+linkTo (Capture rest)    []             = error \"linkTo: capture value needed but the list is empty\" -- :-(\n+linkTo (Verb method)     _              = []\n+```\n+\n+This solution is very unsatisfactory, first and foremost because it is possible\n+for it to error out if we don't supply the right number of captures. However,\n+it will also silently let us pass too many capture values without telling us\n+that some of them are not used. Such a function should be total, we really don't\n+want an implementation that can let us down if we're not very careful.\n+\n+Fortunately, GADTs can help here. We could turn `Endpoint` into","path":"posts/2018-07-12-servant-dsl-typelevel.md","position":180,"original_position":180,"commit_id":"b0020d20b062e1da755768ff906141d0b48dfc28","original_commit_id":"b0020d20b062e1da755768ff906141d0b48dfc28","user":{"login":"alpmestan","id":119323,"node_id":"MDQ6VXNlcjExOTMyMw==","avatar_url":"https://avatars3.githubusercontent.com/u/119323?v=4","gravatar_id":"","url":"https://api.github.com/users/alpmestan","html_url":"https://github.com/alpmestan","followers_url":"https://api.github.com/users/alpmestan/followers","following_url":"https://api.github.com/users/alpmestan/following{/other_user}","gists_url":"https://api.github.com/users/alpmestan/gists{/gist_id}","starred_url":"https://api.github.com/users/alpmestan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alpmestan/subscriptions","organizations_url":"https://api.github.com/users/alpmestan/orgs","repos_url":"https://api.github.com/users/alpmestan/repos","events_url":"https://api.github.com/users/alpmestan/events{/privacy}","received_events_url":"https://api.github.com/users/alpmestan/received_events","type":"User","site_admin":false},"body":"done, a bit below, in an upcoming commit","created_at":"2018-07-12T11:16:18Z","updated_at":"2018-07-12T11:16:18Z","html_url":"https://github.com/haskell-servant/haskell-servant.github.io/pull/58#discussion_r201997975","pull_request_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/pulls/58","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/pulls/comments/201997975"},"html":{"href":"https://github.com/haskell-servant/haskell-servant.github.io/pull/58#discussion_r201997975"},"pull_request":{"href":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/pulls/58"}},"in_reply_to_id":201985990},"pull_request":{"url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/pulls/58","id":200866470,"node_id":"MDExOlB1bGxSZXF1ZXN0MjAwODY2NDcw","html_url":"https://github.com/haskell-servant/haskell-servant.github.io/pull/58","diff_url":"https://github.com/haskell-servant/haskell-servant.github.io/pull/58.diff","patch_url":"https://github.com/haskell-servant/haskell-servant.github.io/pull/58.patch","issue_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/issues/58","number":58,"state":"open","locked":false,"title":"first draft of the typelevel dsl post","user":{"login":"alpmestan","id":119323,"node_id":"MDQ6VXNlcjExOTMyMw==","avatar_url":"https://avatars3.githubusercontent.com/u/119323?v=4","gravatar_id":"","url":"https://api.github.com/users/alpmestan","html_url":"https://github.com/alpmestan","followers_url":"https://api.github.com/users/alpmestan/followers","following_url":"https://api.github.com/users/alpmestan/following{/other_user}","gists_url":"https://api.github.com/users/alpmestan/gists{/gist_id}","starred_url":"https://api.github.com/users/alpmestan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alpmestan/subscriptions","organizations_url":"https://api.github.com/users/alpmestan/orgs","repos_url":"https://api.github.com/users/alpmestan/repos","events_url":"https://api.github.com/users/alpmestan/events{/privacy}","received_events_url":"https://api.github.com/users/alpmestan/received_events","type":"User","site_admin":false},"body":"cc @phadej","created_at":"2018-07-12T01:31:17Z","updated_at":"2018-07-12T11:16:19Z","closed_at":null,"merged_at":null,"merge_commit_sha":"28b74c9bfafb5715de1a77591d5b24c48e5edd9f","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/pulls/58/commits","review_comments_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/pulls/58/comments","review_comment_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/pulls/comments{/number}","comments_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/issues/58/comments","statuses_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/statuses/b0020d20b062e1da755768ff906141d0b48dfc28","head":{"label":"haskell-servant:typelevel-dsl-post","ref":"typelevel-dsl-post","sha":"b0020d20b062e1da755768ff906141d0b48dfc28","user":{"login":"haskell-servant","id":10049283,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEwMDQ5Mjgz","avatar_url":"https://avatars0.githubusercontent.com/u/10049283?v=4","gravatar_id":"","url":"https://api.github.com/users/haskell-servant","html_url":"https://github.com/haskell-servant","followers_url":"https://api.github.com/users/haskell-servant/followers","following_url":"https://api.github.com/users/haskell-servant/following{/other_user}","gists_url":"https://api.github.com/users/haskell-servant/gists{/gist_id}","starred_url":"https://api.github.com/users/haskell-servant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/haskell-servant/subscriptions","organizations_url":"https://api.github.com/users/haskell-servant/orgs","repos_url":"https://api.github.com/users/haskell-servant/repos","events_url":"https://api.github.com/users/haskell-servant/events{/privacy}","received_events_url":"https://api.github.com/users/haskell-servant/received_events","type":"Organization","site_admin":false},"repo":{"id":27440012,"node_id":"MDEwOlJlcG9zaXRvcnkyNzQ0MDAxMg==","name":"haskell-servant.github.io","full_name":"haskell-servant/haskell-servant.github.io","owner":{"login":"haskell-servant","id":10049283,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEwMDQ5Mjgz","avatar_url":"https://avatars0.githubusercontent.com/u/10049283?v=4","gravatar_id":"","url":"https://api.github.com/users/haskell-servant","html_url":"https://github.com/haskell-servant","followers_url":"https://api.github.com/users/haskell-servant/followers","following_url":"https://api.github.com/users/haskell-servant/following{/other_user}","gists_url":"https://api.github.com/users/haskell-servant/gists{/gist_id}","starred_url":"https://api.github.com/users/haskell-servant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/haskell-servant/subscriptions","organizations_url":"https://api.github.com/users/haskell-servant/orgs","repos_url":"https://api.github.com/users/haskell-servant/repos","events_url":"https://api.github.com/users/haskell-servant/events{/privacy}","received_events_url":"https://api.github.com/users/haskell-servant/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/haskell-servant/haskell-servant.github.io","description":"servant website","fork":false,"url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io","forks_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/forks","keys_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/keys{/key_id}","collaborators_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/teams","hooks_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/hooks","issue_events_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/issues/events{/number}","events_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/events","assignees_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/assignees{/user}","branches_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/branches{/branch}","tags_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/tags","blobs_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/git/refs{/sha}","trees_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/git/trees{/sha}","statuses_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/statuses/{sha}","languages_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/languages","stargazers_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/stargazers","contributors_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/contributors","subscribers_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/subscribers","subscription_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/subscription","commits_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/commits{/sha}","git_commits_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/git/commits{/sha}","comments_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/comments{/number}","issue_comment_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/issues/comments{/number}","contents_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/contents/{+path}","compare_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/compare/{base}...{head}","merges_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/merges","archive_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/downloads","issues_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/issues{/number}","pulls_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/pulls{/number}","milestones_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/milestones{/number}","notifications_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/labels{/name}","releases_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/releases{/id}","deployments_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/deployments","created_at":"2014-12-02T15:55:03Z","updated_at":"2018-07-05T13:25:20Z","pushed_at":"2018-07-12T09:41:45Z","git_url":"git://github.com/haskell-servant/haskell-servant.github.io.git","ssh_url":"git@github.com:haskell-servant/haskell-servant.github.io.git","clone_url":"https://github.com/haskell-servant/haskell-servant.github.io.git","svn_url":"https://github.com/haskell-servant/haskell-servant.github.io","homepage":"http://haskell-servant.github.io/","size":656,"stargazers_count":14,"watchers_count":14,"language":"Haskell","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":22,"mirror_url":null,"archived":false,"open_issues_count":10,"license":{"key":"cc-by-4.0","name":"Creative Commons Attribution 4.0","spdx_id":"CC-BY-4.0","url":"https://api.github.com/licenses/cc-by-4.0","node_id":"MDc6TGljZW5zZTI1"},"forks":22,"open_issues":10,"watchers":14,"default_branch":"hakyll"}},"base":{"label":"haskell-servant:hakyll","ref":"hakyll","sha":"ae3658ae22f9ed641d7429500e1628a3fd59e530","user":{"login":"haskell-servant","id":10049283,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEwMDQ5Mjgz","avatar_url":"https://avatars0.githubusercontent.com/u/10049283?v=4","gravatar_id":"","url":"https://api.github.com/users/haskell-servant","html_url":"https://github.com/haskell-servant","followers_url":"https://api.github.com/users/haskell-servant/followers","following_url":"https://api.github.com/users/haskell-servant/following{/other_user}","gists_url":"https://api.github.com/users/haskell-servant/gists{/gist_id}","starred_url":"https://api.github.com/users/haskell-servant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/haskell-servant/subscriptions","organizations_url":"https://api.github.com/users/haskell-servant/orgs","repos_url":"https://api.github.com/users/haskell-servant/repos","events_url":"https://api.github.com/users/haskell-servant/events{/privacy}","received_events_url":"https://api.github.com/users/haskell-servant/received_events","type":"Organization","site_admin":false},"repo":{"id":27440012,"node_id":"MDEwOlJlcG9zaXRvcnkyNzQ0MDAxMg==","name":"haskell-servant.github.io","full_name":"haskell-servant/haskell-servant.github.io","owner":{"login":"haskell-servant","id":10049283,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEwMDQ5Mjgz","avatar_url":"https://avatars0.githubusercontent.com/u/10049283?v=4","gravatar_id":"","url":"https://api.github.com/users/haskell-servant","html_url":"https://github.com/haskell-servant","followers_url":"https://api.github.com/users/haskell-servant/followers","following_url":"https://api.github.com/users/haskell-servant/following{/other_user}","gists_url":"https://api.github.com/users/haskell-servant/gists{/gist_id}","starred_url":"https://api.github.com/users/haskell-servant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/haskell-servant/subscriptions","organizations_url":"https://api.github.com/users/haskell-servant/orgs","repos_url":"https://api.github.com/users/haskell-servant/repos","events_url":"https://api.github.com/users/haskell-servant/events{/privacy}","received_events_url":"https://api.github.com/users/haskell-servant/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/haskell-servant/haskell-servant.github.io","description":"servant website","fork":false,"url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io","forks_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/forks","keys_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/keys{/key_id}","collaborators_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/teams","hooks_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/hooks","issue_events_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/issues/events{/number}","events_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/events","assignees_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/assignees{/user}","branches_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/branches{/branch}","tags_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/tags","blobs_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/git/refs{/sha}","trees_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/git/trees{/sha}","statuses_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/statuses/{sha}","languages_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/languages","stargazers_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/stargazers","contributors_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/contributors","subscribers_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/subscribers","subscription_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/subscription","commits_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/commits{/sha}","git_commits_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/git/commits{/sha}","comments_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/comments{/number}","issue_comment_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/issues/comments{/number}","contents_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/contents/{+path}","compare_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/compare/{base}...{head}","merges_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/merges","archive_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/downloads","issues_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/issues{/number}","pulls_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/pulls{/number}","milestones_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/milestones{/number}","notifications_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/labels{/name}","releases_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/releases{/id}","deployments_url":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/deployments","created_at":"2014-12-02T15:55:03Z","updated_at":"2018-07-05T13:25:20Z","pushed_at":"2018-07-12T09:41:45Z","git_url":"git://github.com/haskell-servant/haskell-servant.github.io.git","ssh_url":"git@github.com:haskell-servant/haskell-servant.github.io.git","clone_url":"https://github.com/haskell-servant/haskell-servant.github.io.git","svn_url":"https://github.com/haskell-servant/haskell-servant.github.io","homepage":"http://haskell-servant.github.io/","size":656,"stargazers_count":14,"watchers_count":14,"language":"Haskell","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":22,"mirror_url":null,"archived":false,"open_issues_count":10,"license":{"key":"cc-by-4.0","name":"Creative Commons Attribution 4.0","spdx_id":"CC-BY-4.0","url":"https://api.github.com/licenses/cc-by-4.0","node_id":"MDc6TGljZW5zZTI1"},"forks":22,"open_issues":10,"watchers":14,"default_branch":"hakyll"}},"_links":{"self":{"href":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/pulls/58"},"html":{"href":"https://github.com/haskell-servant/haskell-servant.github.io/pull/58"},"issue":{"href":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/issues/58"},"comments":{"href":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/issues/58/comments"},"review_comments":{"href":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/pulls/58/comments"},"review_comment":{"href":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/pulls/58/commits"},"statuses":{"href":"https://api.github.com/repos/haskell-servant/haskell-servant.github.io/statuses/b0020d20b062e1da755768ff906141d0b48dfc28"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"alpmestan"}}