F# return type coercion
In F# I have a function that returns System.Linq.Expression instances:
and System.Object with
member this.ToExpression() 开发者_如何学JAVA=
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"
If I omit the type coercions on the return values F# will infer the return type of the function to ConstantExpression. My first thought was to explicitly mark the return type as being : #Expression, but that didn't work. Is there a more elegant way of doing this that doesn't involve manually casting return types to the most generic type?
Thanks.
Edit: Thanks to all of you for the answers. I'll go with the explicit return type + upcast scenario.
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.
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.
Strictly speaking this is not removing the coersion but in my opinion it's a bit better on the eye (and will save you a little bit of typing too :) )
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"
since the type of Constant is a'->Expression it will work in both cases. The down side is of cause that you will have to define a function for each of the Expression factory methods you are going to use.
精彩评论