为了更好地了解F#并更好地了解Suave.io如何工作,我一直试图创建一些可重复使用的函数/运算符来编写函数。我知道Suave实际上实现了它的> =>运算符专门用于异步选项,但我认为尝试概括它会很有趣。
下面的代码受到太多信用来源的启发,它适用于我自己定义的类型,但我无法使其适用于系统类型。即使Nullable和Option的类型扩充编译很好,它们也不会被识别为匹配绑定函数中的成员约束。
当我没有让它适用于Option时,我曾希望可能是由于Option在F#中特殊,这就是为什么我尝试使用Nullable,但遗憾的是,没有雪茄。
fsi的相关错误和输出位于评论中的以下代码中。
任何帮助都将不胜感激。
谢谢, 约翰
open System
let inline bind (f : ^f) (v : ^v) =
(^v : (static member doBind : ^f * ^v -> ^r )(f, v))
// I'd prefer not having to use a tuple in doBind, but I've
// been unable to make multi arg member constraint work
let inline (>=>) f g = f >> (bind g)
// Example with Result
type public Result<'a,'b> =
| Success of 'a
| Error of 'b
type public Result<'a,'b> with
static member inline public doBind (f, v) =
match v with
| Success s -> f s
| Error e -> Error e
let rF a = if a > 0 then Success a else Error "less than 0"
let rG a = if a < 10 then Success a else Error "greater than 9"
let rFG = rF >=> rG
// val rFG : (int -> Result<int,string>)
//> rFG 0;;
//val it : Result<int,string> = Error "less than 0"
//> rFG 1;;
//val it : Result<int,string> = Success 1
//> rFG 10;;
//val it : Result<int,string> = Error "greater than 9"
//> rFG 9;;
//val it : Result<int,string> = Success 9
// So it works as expected for Result
// Example with Nullable
type Nullable<'T when 'T: (new : unit -> 'T) and 'T: struct and 'T:> ValueType> with
static member inline public doBind (f, v: Nullable<'T>) =
if v.HasValue then f v.Value else Nullable()
let nF a = if a > 0 then Nullable a else Nullable()
let nG a = if a < 10 then Nullable a else Nullable()
let nFG = nF >=> nG
// error FS0001: The type 'Nullable<int>' does not support the operator 'doBind'
type Core.Option<'T> with
static member inline doBind (f, v) =
match v with
| Some s -> f s
| None -> None
let oF a = if a > 0 then Some a else None
let oG a = if a < 10 then Some a else None
let oFG = oF >=> oG
// error FS0001: The type 'int option' does not support the operator 'doBind'
答案 0 :(得分:3)
为什么在静态成员约束中不考虑扩展方法是一个多次被问到的问题,并且肯定会在F#编译器中实现该功能之前继续询问它。
请参阅this related question,其中包含指向其他相关问题的链接以及指向F#编译器必须执行的操作的详细说明的链接,以支持此功能。
现在针对您的具体情况,此处提到的解决方法可以解决您的问题,并且已在FsControl中实施。
以下是代码:
#nowarn "3186"
#r "FsControl.dll"
open FsControl.Operators
// Example with Result
type public Result<'a,'b> =
| Success of 'a
| Error of 'b
type public Result<'a,'b> with
static member Return v = Success v
static member Bind (v, f) =
match v with
| Success s -> f s
| Error e -> Error e
let rF a = if a > 0 then Success a else Error "less than 0"
let rG a = if a < 10 then Success a else Error "greater than 9"
let rFG = rF >=> rG
// val rFG : (int -> Result<int,string>)
rFG 0
//val it : Result<int,string> = Error "less than 0"
rFG 1
//val it : Result<int,string> = Success 1
rFG 10
//val it : Result<int,string> = Error "greater than 9"
rFG 9
//val it : Result<int,string> = Success 9
// So it works as expected for Result
// Example with Option
let oF a = if a > 0 then Some a else None
// val oF : a:int -> int option
let oG a = if a < 10 then Some a else None
// val oG : a:int -> int option
let oFG = oF >=> oG
// val oFG : (int -> int option)
oFG 0
// val it : int option = None
oFG 1
// val it : int option = Some 1
无论如何,我建议使用现有的Choice
代替成功/错误,或者implementing Success on top of Choice在您的情况下,它会是这样的:
type Result<'a, 'b> = Choice<'a, 'b>
let Success x :Result<'a, 'b> = Choice1Of2 x
let Error x :Result<'a, 'b> = Choice2Of2 x
let (|Success|Error|) = function Choice1Of2 x -> Success x | Choice2Of2 x -> Error x
然后您可以运行您的示例而无需编写任何绑定或返回。
您可能想知道为什么没有Nullable
的示例,因为Nullable
不是monad,它只适用于值类型而函数不是值类型所以最好坚持Option
以获得相同的功能。