{"action":"created","issue":{"url":"https://api.github.com/repos/fsharp/FSharpLangDesign/issues/49","repository_url":"https://api.github.com/repos/fsharp/FSharpLangDesign","labels_url":"https://api.github.com/repos/fsharp/FSharpLangDesign/issues/49/labels{/name}","comments_url":"https://api.github.com/repos/fsharp/FSharpLangDesign/issues/49/comments","events_url":"https://api.github.com/repos/fsharp/FSharpLangDesign/issues/49/events","html_url":"https://github.com/fsharp/FSharpLangDesign/issues/49","id":133532112,"number":49,"title":"[RFC FS-1004 Discussion] Result type","user":{"login":"enricosada","id":147243,"avatar_url":"https://avatars.githubusercontent.com/u/147243?v=3","gravatar_id":"","url":"https://api.github.com/users/enricosada","html_url":"https://github.com/enricosada","followers_url":"https://api.github.com/users/enricosada/followers","following_url":"https://api.github.com/users/enricosada/following{/other_user}","gists_url":"https://api.github.com/users/enricosada/gists{/gist_id}","starred_url":"https://api.github.com/users/enricosada/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/enricosada/subscriptions","organizations_url":"https://api.github.com/users/enricosada/orgs","repos_url":"https://api.github.com/users/enricosada/repos","events_url":"https://api.github.com/users/enricosada/events{/privacy}","received_events_url":"https://api.github.com/users/enricosada/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":79,"created_at":"2016-02-14T11:38:35Z","updated_at":"2016-05-02T17:20:08Z","closed_at":null,"body":"This issue is used to track discussions of [F# RFC FS-1004 - \"Result type\"](https://github.com/fsharp/FSharpLangDesign/blob/master/RFCs/FS-1004-result-type.md). Please discuss in thread below (if necessary)\r\n"},"comment":{"url":"https://api.github.com/repos/fsharp/FSharpLangDesign/issues/comments/216300450","html_url":"https://github.com/fsharp/FSharpLangDesign/issues/49#issuecomment-216300450","issue_url":"https://api.github.com/repos/fsharp/FSharpLangDesign/issues/49","id":216300450,"user":{"login":"Chicker","id":516518,"avatar_url":"https://avatars.githubusercontent.com/u/516518?v=3","gravatar_id":"","url":"https://api.github.com/users/Chicker","html_url":"https://github.com/Chicker","followers_url":"https://api.github.com/users/Chicker/followers","following_url":"https://api.github.com/users/Chicker/following{/other_user}","gists_url":"https://api.github.com/users/Chicker/gists{/gist_id}","starred_url":"https://api.github.com/users/Chicker/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Chicker/subscriptions","organizations_url":"https://api.github.com/users/Chicker/orgs","repos_url":"https://api.github.com/users/Chicker/repos","events_url":"https://api.github.com/users/Chicker/events{/privacy}","received_events_url":"https://api.github.com/users/Chicker/received_events","type":"User","site_admin":false},"created_at":"2016-05-02T17:20:08Z","updated_at":"2016-05-02T17:20:08Z","body":"\r\nI strongly agree with opinion that the Result type without the implementation of basic functions for working with it is useless. Obviously there are at least two reasons:\r\n* Each time you use the Result type you must have to write a lot of boilerplate code.\r\n* Each user of this type will have to write a minimum of elementary functions for more convenient work with the new type, ie, it will \"reinvent the wheel\".\r\n\r\nA few examples:\r\n\r\n### Example 1\r\n**Only type definition:**\r\n```fsharp\r\nlet getProductById productId : Result<Product, ApiError> =\r\n  let product = Map.tryFind id productList\r\n  match product with\r\n  | Some(p) -> Ok p\r\n  | None -> Error <| ProductNotFoundError id\r\n```\r\n\r\n**With base combinators**\r\n```fsharp\r\nlet getProductById productId : Result<Product, ApiError> =\r\n  let product = Map.tryFind id productList\r\n\r\n  Result.ifNone (ProductNotFoundError id) product\r\n```\r\n\r\n### Example 2\r\n**Only type definition:**\r\n```fsharp\r\nlet getProductCost productId : Result<float, ApiError> =\r\n  let product = getProductById id\r\n  match product with\r\n  | Ok p -> Ok p.Cost\r\n  | Error e -> Error e\r\n```\r\n\r\n**With base combinators**\r\n```fsharp\r\nlet getProductCost productId : Result<float, ApiError> =\r\n  let product = getProductById id\r\n  \r\n  Result.map (fun p -> Ok p.Cost) product\r\n```\r\n\r\n### Example 3\r\n**Only type definition:**\r\n```\r\nlet calcDiscountTotal1 prod1Id prod2Id discount : Result<float, ApiError> =\r\n  let product1 = getProductById prod1Id\r\n  let product2 = getProductById prod2Id\r\n  \r\n  match (product1, product2) with\r\n  | (Ok p1, Ok p2) ->\r\n      let (c1, c2) = minByCost p1 p2\r\n      Ok <| c1 - (discount / 100.0 * c1) + c2\r\n  | (Error e1, _) -> Error e1\r\n  | (_, Error e2) -> Error e2\r\n```\r\n\r\n**With base combinators and computation expression**\r\n```fsharp\r\nlet calcDiscountTotal1 prod1Id prod2Id discount : Result<float, ApiError> =\r\n  result {\r\n    let! product1 = getProductById prod1Id\r\n    let! product2 = getProductById prod2Id \r\n    let (c1, c2) = minByCost product1 product2\r\n    return (c1 - (discount / 100.0 * c1) + c2)\r\n  }\r\n```\r\n\r\n#### Minimal implementation of Result type with some combinators and computation expression.\r\n\r\n```fsharp\r\nmodule Result =\r\n  type Result<'TResult, 'TError> =\r\n    | Ok of 'TResult\r\n    | Error of 'TError\r\n  \r\n  let ifNone (error : 'TError) = function\r\n    | Some (o) -> Ok o\r\n    | None -> Error error\r\n  \r\n  let map f = function\r\n    | Ok v -> f v\r\n    | Error e -> Error e\r\n  \r\n  let bind m f =\r\n    match m with\r\n    | Ok v -> f v\r\n    | Error e -> Error e\r\n  \r\n  let retn v = Ok v\r\n  \r\n  let retnFrom v = v\r\n    \r\n  let tryResult (body, err) =\r\n    try\r\n      retn <| body()\r\n    with e -> Error err\r\n  \r\n  type ResultBuilder () =\r\n    member __.Zero () = Error\r\n    member __.Bind (m , f) = bind m f\r\n    member __.Return (v) = retn v\r\n    member __.ReturnFrom (v) = retnFrom v\r\n    member __.Delay (f) = fun () -> f ()\r\n    member __.Run (f) = f()\r\n    member __.TryWith (body, handler) =\r\n      try\r\n        __.Run body\r\n      with e -> handler e\r\n\r\n    member __.TryFinally (body, handler) =\r\n      try\r\n        __.Run body\r\n      finally\r\n        handler ()\r\n    \r\n  let result = ResultBuilder()\r\n```\r\n\r\n"}}