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.