静态解析的类型成员约束无法识别系统类型的扩充

时间:2016-08-17 21:30:32

标签: f# inline

为了更好地了解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'

1 个答案:

答案 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以获得相同的功能。