这些F#函数应该做什么?

Eho*_*ret 1 f#

学习F#这几天,我注意到,在像一些图书馆这一个那一个 有这似乎是常见的F#,但真的不能解密他们,他们在做什么,他们是什么样的一些类似的功能呢?

let ap x f =
    match f, x with
    | Ok f        , Ok x    -> Ok (f x)
    | Error e     , _       -> Error e
    | _           , Error e -> Error e
let inline (<*>) f x = ap x f
let inline (<!>) f x = Result.map f x
let inline lift2 f a b = f <!> a <*> b
Run Code Online (Sandbox Code Playgroud)

即使与他们汇总评论对我的理解也无济于事:

/// Sequential application
/// If the wrapped function is a success and the given result is a success the function is applied on the value. 
/// Otherwise the exisiting error messages are propagated.
let ap x f =
match f,x with
    | Ok f        , Ok x    -> Ok (f x)
    | Error e     , _       -> Error e
    | _           , Error e -> Error e

/// Sequential application
/// If the wrapped function is a success and the given result is a success the function is applied on the value. 
/// Otherwise the exisiting error messages are propagated.
let inline (<*>) f x = ap x f

/// Infix map, lifts a function into a Result and applies it on the given result.
let inline (<!>) f x = Result.map f x

/// Promote a function to a monad/applicative, scanning the monadic/applicative arguments from left to right.
let inline lift2 f a b = f <!> a <*> b
Run Code Online (Sandbox Code Playgroud)

我什至没有看到如何使用它们的示例,也不确定为什么inline使用它们。

如果有人可以暗示这些功能的实用性,我将不胜感激。

dum*_*ulo 5

斯科特·沃斯钦(Scott Wlaschin)的F#(寓教于乐)(https://fsharpforfunandprofit.com)提供了一系列地图和绑定并应用,噢,我的天!https://fsharpforfunandprofit.com/posts/elevated-world-7),它应该能够对此有所启发。关于您的特定问题:

  • <!>是将map函数f和参数应用于x要映射的数据结构的元素的运算符,换句话说,就是将函数提升到数据结构的领域(在这种情况下为Result类型)。
  • <*>ap(应用)运算符,用于将包装在升高值内的函数解压缩为提升函数。
  • lift2基本上map是两参数函数的运算符。

请看一下博客,它确实有帮助!


Tar*_*mil 5

这些被称为“应用函子”(有时也称为“应用函子”)。它们的目的是Something<'T>使用一个函数合并多个数据。基本上,将类型'Arg1 -> 'Arg2 -> ... -> 'Result的功能“提升”为类型的功能Something<'Arg1> -> Something<'Arg2> -> ... -> Something<'Result>

例如,给定标准的Res​​ult类型:

type Result<'T, 'Err> = Ok of 'T | Error of 'Err
Run Code Online (Sandbox Code Playgroud)

您可能有几个要组合在一起的结果值。例如,假设您有一个带有输入firstName,lastName和age的表单。您还具有结果类型Person

type Person = { firstName: string; lastName: string; age: int }

// string -> string -> int -> Person
let makePerson firstName lastName age =
    { firstName = firstName; lastName = lastName; age = age }
Run Code Online (Sandbox Code Playgroud)

来自您实际形式的值可能具有类型Result<string, InputError>Result<int, InputError>Error例如。用户尚未输入值。

type InputError =
    | FieldMissing of fieldName: string
    // Other error cases...
Run Code Online (Sandbox Code Playgroud)

您想将它们组合成一个Result<Person, InputError>Ok如果所有输入都是Ok,或者Error任何输入都是Error。使用该应用程序,您可以这样做:

// Result<string, InputError> -> Result<string, InputError> -> Result<int, InputError> -> Result<Person, InputError>
let makePersonResult firstName lastName age =
    makePerson <!> firstName <*> lastName <*> age

// Example uses:

makePersonResult (Ok "John") (Ok "Doe") (Ok 42)
// --> Ok { firstName = "John"; lastName = "Doe"; age = 42 }
makePersonResult (Error (FieldMissing "firstName")) (Ok "Doe") (Ok 42)
// --> Error (FieldMissing "firstName")
Run Code Online (Sandbox Code Playgroud)

除了Result之外,类似的概念还可以应用于许多其他类型,这就是为什么要为其指定名称的原因。例如,一个on的应用程序Async<'T>可以并行运行所有参数Asyncs,并在完成后将其结果合并为Async<'Result>。另一个例子是,applicative 'T list等同于标准库List.map2List.map3但可以泛化为任意数量的参数列表。

旁注:如果您查找“应用函子”,您将找到的大多数结果将在Haskell中,在该处通常<!>使用F#编写的map运算符被<$>代替。