Приведение возвращаемого типа F#

В 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.


person Alex    schedule 08.05.2010    source источник


Ответы (3)


Вот несколько способов, которые вы можете предпочесть:

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, который выведет тип для преобразования из ограничений.

person Brian    schedule 08.05.2010

Я не думаю, что есть значительно более элегантный способ написать это, к сожалению.

Компилятор требует, чтобы все ветви выражения 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 является ключевым словом, поэтому вы не можете использовать его с конвейерной обработкой, поэтому определение такой функции может иметь некоторые преимущества.

person Tomas Petricek    schedule 08.05.2010

Строго говоря, это не устраняет принуждение, но, на мой взгляд, это немного лучше для глаз (и также сэкономит вам немного времени на наборе текста :))

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, которые вы собираетесь использовать.

person Rune FS    schedule 08.05.2010