Ответ 1
Это интересный вопрос!
Существующий код работает очень хорошо, но я бы сделал одно изменение - вам фактически не нужно передавать фактические значения FooResult
и BarResult
. Вы можете определить тип MarkedType<'TPhantom, 'TValue>
, который представляет значение 'TValue
со специальной "меткой", указанной другим типом:
type MarkedValue<'TPhantom, 'TValue> = Value of 'TValue
Затем вы можете использовать интерфейсы как параметры типа для типа phantom. Мне было сложно подумать о "результатах", поэтому я буду использовать входы вместо:
type IFooInput = interface end
type IBarInput = interface end
Теперь уловка заключается в том, что вы также можете определить интерфейс, который является как IFooInput
, так и IBarInput
:
type IFooOrBarInput =
inherit IFooInput
inherit IBarInput
Итак, теперь вам нужно добавить соответствующие аннотации к foo
и bar
:
let foo param (Value v : MarkedValue<#IFooInput, _>) : MarkedValue<IBarInput, _> =
Value 0
let bar param (Value v : MarkedValue<#IBarInput, _>) : MarkedValue<IFooOrBarInput, _> =
Value 0
Здесь аннотация на входе говорит, что она должна принимать все, что есть или наследуется от IFooInput
или IBarInput
. Но результат функции bar
отмечен IFooOrBarInput
, что позволяет передать его как на foo
, так и на bar
:
(Value 0 : MarkedValue<IFooInput, _>)
|> foo 10
|> bar "A"
|> bar "A"
|> foo 20
|> bar "B"