在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场景。
答案 0 :(得分:4)
您可能会选择以下两种方式:
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"
通过在member
声明中明确说明返回类型,您可以在正文中推断它,例如通过_
作为“请为我推断此类型”或使用upcast
运算符,该运算符将推断从约束中向上转换的类型。
答案 1 :(得分:1)
我认为没有任何明显更优雅的写作方式,不幸的是。
编译器要求match
表达式的所有分支都具有相同的返回类型,并且不会隐式插入任何强制。您可以使用upcast
关键字插入强制而不指定目标类型 - 在这种情况下,编译器将使用其他信息(例如类型注释)来确定类型,您不必重复类型:
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"
我为每个表达式和类型注释添加了类型注释和upcast
,因此F#编译器推断upcast
需要将结果强制转换为Expression
。编译器插入隐式强制的唯一地方是在调用函数时,所以你也可以编写以下内容(但我不确定它是否更好):
// 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
出于某种原因,upcast
是一个关键字,因此您无法将其用于流水线操作,因此定义此类函数可能会带来一些好处。
答案 2 :(得分: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"
因为常量的类型是' - >表达式,所以它在两种情况下都有效。缺点是你必须为你将要使用的每个Expression工厂方法定义一个函数。