如何从obj转发到选项<obj>?

FSh*_*00b 10 f# casting

我有一个函数,它接受一个object类型的参数,需要将它向下转换为option<obj>.

member s.Bind(x : obj, rest) =
    let x = x :?> Option<obj>
Run Code Online (Sandbox Code Playgroud)

如果我传递(例如)Option<string>as x,则最后一行抛出异常:无法转换类型为"Microsoft.FSharp.Core.FSharpOption'1 [System.String]"的对象以键入"Microsoft.FSharp.Core.FSharpOption" 1 [System.Object的]".

或者,如果我尝试进行类型测试:

member s.Bind(x : obj, rest) =
   match x with
    | :? option<obj> as x1 -> ... // Do stuff with x1
    | _ -> failwith "Invalid type"
Run Code Online (Sandbox Code Playgroud)

然后x从不匹配option<obj>.

为了使这项工作,我当前必须指定选项包含的类型(例如,如果函数被传递option<string>,并且我将参数向下转换为而不是option<obj>,函数可以工作.

有没有一种方法可以将参数向下转换为option<obj>不指定选项包含的类型?我试过了option<_>,option<#obj>并且option<'a>得到了相同的结果.

作为背景,参数需要是类型的,obj因为我正在为monad编写接口,因此Bind需要根据实现接口的monad绑定不同类型的值.这个特殊的monad是一个延续monad,所以它只是想确定参数是否Some(x)存在None,然后传递x给休息.(我需要接口的原因是因为我正在编写一个monad转换器,我需要一种方法告诉它它的参数monad实现bind和return.)

更新:我成功通过向上转换选项的内容,然后它成为此函数的参数,但我仍然很想知道我是否可以对选项进行类型测试或转换对象(或通用参数)不必担心选项包含的类型(当然假设转换是有效的,即对象确实是一个选项).

Tom*_*cek 7

目前没有任何好办法解决这个问题.

问题是你需要在模式匹配中引入一个新的泛型类型参数(当匹配时option<'a>),但F#只允许你在函数声明中定义泛型类型参数.所以,你唯一的解决方案是使用一些反射技巧.例如,您可以定义隐藏此的活动模式:

let (|SomeObj|_|) =
  let ty = typedefof<option<_>>
  fun (a:obj) ->
    let aty = a.GetType()
    let v = aty.GetProperty("Value")
    if aty.IsGenericType && aty.GetGenericTypeDefinition() = ty then
      if a = null then None
      else Some(v.GetValue(a, [| |]))
    else None
Run Code Online (Sandbox Code Playgroud)

这将为您提供NoneSome包含obj任何选项类型:

let bind (x : obj) rest =   
    match x with    
    | SomeObj(x1) -> rest x1
    | _ -> failwith "Invalid type"

bind(Some 1) (fun n -> 10 * (n :?> int))
Run Code Online (Sandbox Code Playgroud)