2

在 F# 中,我有一个返回 System.Linq.Expression 实例的函数:

and System.Object with
  member this.ToExpression() = 
    match this with
    | :? System.Int32 -> Expression.Constant(this) :> Expression
    | :? System.Boolean -> Expression.Constant(this) :> Expression
    | :? Tml.Runtime.Seq as s -> s.ToExpression()
    | _ -> failwith "bad expression"

如果我省略了返回值的类型强制,F# 会将函数的返回类型推断为 ConstantExpression。我的第一个想法是将返回类型显式标记为:#Expression,但这不起作用。是否有一种更优雅的方式来执行此操作,而不涉及手动将返回类型转换为最通用的类​​型?

谢谢。

编辑:感谢大家的回答。我将使用显式返回类型 + upcast 场景。

4

3 回答 3

5

Here are a couple ways you might prefer:

open System.Linq.Expressions 

type System.Object with
    member this.ToExpression() : Expression =  // explicit
        match this with 
        | :? System.Int32 -> upcast Expression.Constant(this) // upcast
        | :? System.Boolean -> Expression.Constant(this) :> _ // _
        | _ -> failwith "bad expression"

By explicitly stating the return type on the member declaration, you can then infer it in the body, e.g. via _ as a "please infer this type for me" or by using the upcast operator which will infer the type to up-cast to from the constraints.

于 2010-05-08T16:59:36.273 回答
1

严格来说,这并没有消除强制,但在我看来,它在眼睛上会更好一些(并且也会为你节省一点打字时间:))

open System.Linq.Expressions

let Constant obj = Expression.Constant(obj) :> Expression

type System.Object with
    member this.ToExpression()
        match this with 
        | :? System.Int32 -> Constant(this)
        | :? System.Boolean -> Constant(this)
        | _ -> failwith "bad expression"

因为 Constant 的类型是 a'->Expression 它在这两种情况下都可以工作。不利的一面是您必须为要使用的每个表达式工厂方法定义一个函数。

于 2010-05-08T19:58:22.683 回答
1

I don't think there is any significantly more elegant way of writing this, unfrotunately.

The compiler requires that all branches of the match expression will have the same return type and it doesn't implicitly insert any coercions. You can use the upcast keyword to insert a coercion without specifying the target type - in this case, the compiler will use other information (such as type annotations) to determine the type and you won't have to repeat the type:

and System.Object with 
  member this.ToExpression() : Expression =  
    match this with 
    | :? System.Int32 -> upcast Expression.Constant(this) 
    | :? System.Boolean -> upcast Expression.Constant(this)
    | :? Tml.Runtime.Seq as s -> upcast s.ToExpression() 
    | _ -> failwith "bad expression" 

I added type annotation and upcast to each of the expression and type annotation, so the F# compiler infers that the upcast needs to coerce the result to Expression. The only place where the compiler inserts implicit coercions is when calling a function, so you could also write the following (but I'm not sure if it's any better):

// Thanks to implicit coercions, we don't even need #type
let expr (a:Expression) = a

// and then for example:
| :? System.Int32 -> Expression.Constant(this) |> expr

For some reason, upcast is a keyword, so you cannot use it with pipelining, so definining a function like this may have some benefits.

于 2010-05-08T16:59:45.727 回答