Sol*_*lma 4 f# optional f#-4.1
F#语言包含Discriminated Union类型option<'T>.几个模块包含有用的函数,XYZ.tryFind其返回值是类型的对象option<'T>.(实施例:List.tryFind,Map.tryFind,Array.tryFind).F# 4.1添加了Result<'S,'T>类似option<'T>但提供更多信息的类型.是否有类似于tryFind该Result<'S,'T>类型的功能?
下面的代码是尝试创建这样的功能.
let resultFind (ef: 'K -> 'T) (tryfind: 'K -> 'M -> 'T option) (m: 'M) (k: 'K) =
    let y = tryfind k m
    match y with
    | Some i -> Result.Ok i
    | None -> Result.Error (ef k)
let fields = [("Email", "jdoe@xyz.com"); ("Name", "John Doe")]
let myMap = fields |> Map.ofList
let ef k = sprintf "%s %A" "Map.tryFind called on myMap with bad argument " k
let rF = resultFind ef Map.tryFind myMap // analogous to tryFind
rF "Name"
rF "Whatever"
val resultFind :
  ef:('K -> 'T) ->
    tryfind:('K -> 'M -> 'T option) -> m:'M -> k:'K -> Result<'T,'T>
val fields : (string * string) list =
  [("Email", "jdoe@xyz.com"); ("Name", "John Doe")]
val myMap : Map<string,string> =
  map [("Email", "jdoe@xyz.com"); ("Name", "John Doe")]
val ef : k:'a -> string
val rF : (string -> Result<string,string>)
[<Struct>]
val it : Result<string,string> = Ok "John Doe"
[<Struct>]
val it : Result<string,string> =
  Error "Map.tryFind called on myMap with bad argument  "Whatever"" 
此外,为什么[<Struct>]声明出现在Result对象上方?
标准库没有这些功能,也不应该.有了这样的功能tryFind,实际上只有一件事可能出错:无法找到价值.因此,实际上不需要全面表示错误情况.只需一个简单的"是/否"信号即可.
但是,在已知的上下文中,您需要使用特定的错误信息"标记"失败,以便您可以将其传递给更高级别的使用者,这是一个合法的用例.
但是,对于这个用例,为每个函数创建一个包装器将是浪费和重复的:除了它们调用的函数之外,这些包装器将完全相同.通过使你的函数成为一个更高阶的函数,你的尝试是正确的方向,但它还远远不够:即使你接受函数作为参数,你已经"烘焙"了该函数的形状.当你发现自己需要使用两个参数的函数时,你必须复制并粘贴包装器.这最终来自于您的函数负责两个方面 - 调用函数和转换结果.没有另一个,你不能重复使用.
让我们尝试进一步扩展方法:将问题分解成更小的部分,然后将它们组合在一起以获得完整的解决方案.
首先,让我们自己创造一种将Option价值"转换"为一种价值的方法Result.显然,我们需要提供错误的值:
module Result =
    let ofOption (err: 'E) (v: 'T option) =
        match v with
        | Some x -> Ok x
        | None -> Error err
现在我们可以用它来转换Option成Result:
let r = someMap |> Map.tryFind k |> 
        Result.ofOption (sprintf "Key %A couldn't be found" k)
到现在为止还挺好.但接下来要注意的是,并不总是需要错误值,因此每次计算它都会很浪费.让我们推迟这个计算:
module Result =
    let ofOptionWith (err: unit -> 'E) (v: 'T option) =
        match v with
        | Some x -> Ok x
        | None -> Error (err())
    let ofOption (err: 'E) = ofOptionWith (fun() -> err)
现在我们仍然可以ofOption在错误值计算成本时使用,但我们也可以使用ofOptionWith以下命令推迟计算:
let r = someMap |> Map.tryFind k 
        |> Result.ofOptionWith (fun() -> sprintf "Key %A couldn't be found" k)
接下来,我们可以使用这个转换函数来创建返回函数的包装器Option,使它们返回Result:
module Result =
    ...
    let mapOptionWith (err: 'a -> 'E) (f: 'a -> 'T option) a =
       f a |> ofOptionWith (fun() -> err a)
现在我们可以rF根据以下方面定义您的功能Result.mapOptionWith:
let rF = Result.mapOptionWith
           (sprintf "Map.tryFind called on myMap with bad argument %s")
           (fun k -> Map.tryFind k myMap)
| 归档时间: | 
 | 
| 查看次数: | 267 次 | 
| 最近记录: |