6

I'm trying to work with expressions in F# (the System.Linq.Expression kind). Here is a quick example of the problem I am having:

let blah = seq {
    yield Expression.New(typedefof<'T>)
    yield Expression.Constant(1)
}

What I would like is for blah to be a seq<Expression>. However the sequence infers its type by the first yield, which is a NewExpression. This will cause the second yield to result in a compilation failure because it is a ConstantExpression. A working solution is to upcast all of the yields:

let blah = seq<Expression> {
    yield Expression.New(typedefof<'T>) :> Expression
    //or
    yield upcast Expression.Constant(1)
}

But that feels unwieldy, having to upcast every single time. I thought flexible types might be a possible solution as well, but I've had trouble with that as well. seq<#Expression> doesn't seem to work.

Is there a way I can generate a sequence of expressions without having to upcast every single one of them?

vcsjones
  • 138,677
  • 31
  • 291
  • 286
  • 1
    F# doesn't implicitly upcast in most situations, so I don't see the upcast keyword as unwieldy, but I don't know if there's a different way to go about it either. – jbtule Nov 19 '13 at 14:41
  • 3
    I think [this question](http://stackoverflow.com/questions/16714567/type-inference-in-sequence-expressions-in-f) also covers your case, especially if you don't really need a *sequence* but could use an array or list, you can add [a type annotation and it should work](http://stackoverflow.com/a/16718425/1180426)... – Patryk Ćwiek Nov 19 '13 at 14:50
  • While this was presented with a trivial example, using sequences has its advantages for me since I'll be utilizing `yield!`, but perhaps a list is a workable solution. That answer also helped me understand why the F# compiler is a little funny about the subject. – vcsjones Nov 19 '13 at 14:54
  • 1
    A wee bit more readable variant: `let blah = seq { yield Expression.New(typedefof<'T>) :> _ ; yield Expression.Constant(1) :> _ }` – Eugene Fotin Nov 19 '13 at 15:17

1 Answers1

8

As others already mentioned, F# does not usually automatically insert upcasts in your code, so you'll need to add a type annotation and cast or the upcast keyword - I think the comments already cover all the options.

There are two cases when the compiler does upcasts - one is when passing argument to a function and the other is when you create an array or a list literal. The second can actually be used to simplify your example:

let blah<'T> : Expression list = 
  [ Expression.New(typedefof<'T>) 
    Expression.Constant(1) ]

Here, the compiler inserts upcast to Expression automatically. I suppose your actual use case is more complicated - when you need sequence expressions with yield, this will not work because then you're writing sequence expresions rather than list literals

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553