{"action":"created","issue":{"url":"https://api.github.com/repos/fable-compiler/fable-powerpack/issues/29","repository_url":"https://api.github.com/repos/fable-compiler/fable-powerpack","labels_url":"https://api.github.com/repos/fable-compiler/fable-powerpack/issues/29/labels{/name}","comments_url":"https://api.github.com/repos/fable-compiler/fable-powerpack/issues/29/comments","events_url":"https://api.github.com/repos/fable-compiler/fable-powerpack/issues/29/events","html_url":"https://github.com/fable-compiler/fable-powerpack/pull/29","id":250163792,"number":29,"title":"[WIP] Provide an easy way to write documentation","user":{"login":"MangelMaxime","id":4760796,"avatar_url":"https://avatars0.githubusercontent.com/u/4760796?v=4","gravatar_id":"","url":"https://api.github.com/users/MangelMaxime","html_url":"https://github.com/MangelMaxime","followers_url":"https://api.github.com/users/MangelMaxime/followers","following_url":"https://api.github.com/users/MangelMaxime/following{/other_user}","gists_url":"https://api.github.com/users/MangelMaxime/gists{/gist_id}","starred_url":"https://api.github.com/users/MangelMaxime/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MangelMaxime/subscriptions","organizations_url":"https://api.github.com/users/MangelMaxime/orgs","repos_url":"https://api.github.com/users/MangelMaxime/repos","events_url":"https://api.github.com/users/MangelMaxime/events{/privacy}","received_events_url":"https://api.github.com/users/MangelMaxime/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":2,"created_at":"2017-08-14T21:46:50Z","updated_at":"2017-08-19T00:20:19Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/fable-compiler/fable-powerpack/pulls/29","html_url":"https://github.com/fable-compiler/fable-powerpack/pull/29","diff_url":"https://github.com/fable-compiler/fable-powerpack/pull/29.diff","patch_url":"https://github.com/fable-compiler/fable-powerpack/pull/29.patch"},"body":"This PR aims to provide an easy way to write documentation.\r\n\r\nIt's use [Fornax](https://gitlab.com/Krzysztof-Cieslak/Fornax) which is a static site generator define via F#. The DSL is really close to the one use in Fable.React.\r\n\r\nHere is a GIF showing the current layout status and also how easy it is to add a new page. We can customize almost anything as Fornax use F# fsi to generate the website.\r\n\r\n![fable_powerpack_fornax_demo](https://user-images.githubusercontent.com/4760796/29293236-a900480e-814a-11e7-82ac-b9f859af5949.gif)\r\n\r\nPS: As always, I use Bulma for the CSS framework :smiley: "},"comment":{"url":"https://api.github.com/repos/fable-compiler/fable-powerpack/issues/comments/323487075","html_url":"https://github.com/fable-compiler/fable-powerpack/pull/29#issuecomment-323487075","issue_url":"https://api.github.com/repos/fable-compiler/fable-powerpack/issues/29","id":323487075,"user":{"login":"Pauan","id":97238,"avatar_url":"https://avatars2.githubusercontent.com/u/97238?v=4","gravatar_id":"","url":"https://api.github.com/users/Pauan","html_url":"https://github.com/Pauan","followers_url":"https://api.github.com/users/Pauan/followers","following_url":"https://api.github.com/users/Pauan/following{/other_user}","gists_url":"https://api.github.com/users/Pauan/gists{/gist_id}","starred_url":"https://api.github.com/users/Pauan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Pauan/subscriptions","organizations_url":"https://api.github.com/users/Pauan/orgs","repos_url":"https://api.github.com/users/Pauan/repos","events_url":"https://api.github.com/users/Pauan/events{/privacy}","received_events_url":"https://api.github.com/users/Pauan/received_events","type":"User","site_admin":false},"created_at":"2017-08-19T00:20:19Z","updated_at":"2017-08-19T00:20:19Z","body":"@MangelMaxime I would write that example like this:\r\n\r\n```fsharp\r\nlet unwrapNumber a =\r\n    match a with\r\n    | Json.Number a -> a\r\n    | _ -> failwith \"Invalid JSON, it must be a number\"\r\n\r\nlet unwrapString a =\r\n    match a with\r\n    | Json.String a -> a\r\n    | _ -> failwith \"Invalid JSON, it must be a string\"\r\n\r\nlet unwrapObject a =\r\n    match a with\r\n    | Json.Object a -> Map.ofArray a\r\n    | _ -> failwith \"Invalid JSON, it must be an object\"\r\n\r\nlet unwrapArray a =\r\n    match a with\r\n    | Json.Array a -> a\r\n    | _ -> failwith \"Invalid JSON, it must be an array\"\r\n```\r\n\r\n```fsharp\r\nopen Fable.PowerPack\r\nopen Fable.PowerPack.Result\r\n\r\n\r\ntype UserDef =\r\n    { firstname : string\r\n      country : string }\r\n\r\ntype JsonDef =\r\n    { size : int\r\n      users : UserDef list }\r\n\r\n\r\nlet jsonTxt = \r\n    \"\"\"\r\n{\r\n    size: 3\r\n    users: [\r\n        {\r\n            firstname: \"Maxime\",\r\n            country: \"France\"\r\n        },\r\n        {\r\n            firstname: \"Alfonso\",\r\n            country: \"Spain\"\r\n        },\r\n        {\r\n            firstname: \"Forki\",\r\n            country: \"Germany\"\r\n        }\r\n    ]\r\n}        \r\n    \"\"\"\r\n\r\n\r\nlet json = jsonTxt |> Json.ofString |> unwrapResult |> unwrapObject\r\n\r\nlet sizeValue = json |> Map.find \"size\" |> unwrapNumber\r\nlet users = json |> Map.find \"users\" |> unwrapArray\r\n\r\nlet userList =\r\n    [ for user in users do\r\n        let obj = user |> unwrapObject\r\n        let firstname = obj |> Map.find \"firstname\" |> unwrapString\r\n        let country = obj |> Map.find \"country\" |> unwrapString\r\n        yield { firstname = firstname\r\n                country = country }\r\n    ]\r\n\r\n{ size = int sizeValue\r\n  users = userList }\r\n```\r\n\r\nThe first code is utility functions which should probably be in `fable-powerpack`, and the second code is the actual user code.\r\n\r\nThe benefit of the `Json` module is that it uses ADTs, so it forces you to correctly handle every possibility.\r\n\r\nBecause of that you have to use functions like `unwrapString`. This guarantees that it will always throw exceptions if the JSON is wrong.\r\n\r\nThe above code uses exceptions, but you can also use `Result` for even more type safety:\r\n\r\n```fsharp\r\nlet number a =\r\n    match a with\r\n    | Json.Number a -> Ok(a)\r\n    | _ -> Error(System.Exception(\"Invalid JSON, it must be a number\"))\r\n\r\nlet string a =\r\n    match a with\r\n    | Json.String a -> Ok(a)\r\n    | _ -> Error(System.Exception(\"Invalid JSON, it must be a string\"))\r\n\r\nlet lookup (key: string) (a: Map<string, Json.Json>) =\r\n    match Map.tryFind key a with\r\n    | Some(a) -> Ok(a)\r\n    | None -> Error(System.Exception(\"Could not find key \" + key))\r\n\r\nlet object a =\r\n    match a with\r\n    | Json.Object a -> Ok(Map.ofArray a)\r\n    | _ -> Error(System.Exception(\"Invalid JSON, it must be an object\"))\r\n\r\nlet array a =\r\n    match a with\r\n    | Json.Array a -> Ok(a)\r\n    | _ -> Error(System.Exception(\"Invalid JSON, it must be an array\"))\r\n```\r\n\r\n```fsharp\r\nopen Result\r\nopen Fable.PowerPack\r\nopen Fable.PowerPack.Result\r\n\r\n\r\ntype UserDef =\r\n    { firstname : string\r\n      country : string }\r\n\r\ntype JsonDef =\r\n    { size : int\r\n      users : UserDef list }\r\n\r\n\r\nlet jsonTxt = \r\n    \"\"\"\r\n{\r\n    size: 3\r\n    users: [\r\n        {\r\n            firstname: \"Maxime\",\r\n            country: \"France\"\r\n        },\r\n        {\r\n            firstname: \"Alfonso\",\r\n            country: \"Spain\"\r\n        },\r\n        {\r\n            firstname: \"Forki\",\r\n            country: \"Germany\"\r\n        }\r\n    ]\r\n}        \r\n    \"\"\"\r\n\r\n\r\nresult {\r\n    let! json = jsonTxt |> Json.ofString |> bind object\r\n\r\n    let! sizeValue = json |> lookup \"size\" |> bind number\r\n    let! users = json |> lookup \"users\" |> bind array\r\n\r\n    let! userList =\r\n        seq {\r\n            for user in users ->\r\n                result {\r\n                    let! obj = user |> object\r\n                    let! firstname = obj |> lookup \"firstname\" |> bind string\r\n                    let! country = obj |> lookup \"country\" |> bind string\r\n                    return { firstname = firstname\r\n                             country = country }\r\n                }\r\n        }\r\n\r\n    return { size = int sizeValue\r\n             users = List.ofSeq userList }\r\n}\r\n```\r\n\r\n(The above code doesn't work yet, it requires some changes to the `Fable.PowerPack.Result` module first)\r\n\r\nOf course you can use pattern matching as well, if you want to handle multiple different types:\r\n\r\n```fsharp\r\nmatch a with\r\n| Json.Number a -> ...\r\n| Json.String a -> ...\r\n| Json.Array a -> ...\r\n| _ -> failwith \"Invalid JSON\"\r\n```\r\n\r\nBasically it lets you parse JSON with idiomatic F# tools like pattern matching, exceptions, and `Result`\r\n\r\nIt is a bit slow, because it has to create the ADT wrappers. You could create a different JSON parsing library which uses dynamic casts. It would be faster, but it would lose the safety of pattern matching."}}
{"actor":{"display_login":"Pauan"}}