Вы можете сделать что-то в том же духе, потребовав, чтобы ваши векторные типы (а также функции на векторах) реализовали определенные операторы. Исходя из вашего примера, я полагаю, у вас уже есть +
для Vec2
и *
для умножения вектора на скаляр. Вы можете написать функцию average
в терминах этих операторов, и тогда она будет работать с любым типом, у которого есть эти операторы.
Единственная проблема заключается в том, что F # обрабатывает *
в некотором особом порядке, и поэтому выне может легко сделать это, если у вас есть *
типа float * vector -> vector
. Кажется, это работает нормально, если вы используете .*
для скалярного умножения на вектор (и аналогично, вы можете добавить *.
для другого направления).
Вот мои определения Vec2
и Vec3
с этими операторами:
type Vec2(a:float, b:float) =
member x.A = a
member x.B = b
static member (.*) (a:float, v:Vec2) =
Vec2(a*v.A, a*v.B)
static member (+) (v1:Vec2, v2:Vec2) =
Vec2(v1.A+v2.A, v1.B+v2.B)
type Vec3(a:float, b:float, c:float) =
member x.A = a
member x.B = b
member x.C = c
static member (.*) (a:float, v:Vec3) =
Vec3(a*v.A, a*v.B, a*v.C)
static member (+) (v1:Vec3, v2:Vec3) =
Vec3(v1.A+v2.A, v1.B+v2.B, v1.C+v2.C)
Теперь вы можете написать average
как встроенную функцию, которая использует статические ограничения членов:
let inline average (v1:^V) (v2:^V) =
(^V : (static member (.*) : float * ^V -> ^V) (0.5, v1 + v2))
average (Vec2(1.,1.)) (Vec2(3.,4.))
average (Vec3(1.,1.,1.)) (Vec3(3.,4.,5.))
F # автоматически добавляет ограничение, если вы используете +
оператор, поэтому я могу просто написать v1 + v2
. Оператор .*
является нестандартным, и поэтому мне пришлось вызывать его явно.
Для второй части вашего вопроса - как вы заметили, типы F # не могут параметризоваться другими типами с ограничениями статического типа, поэтомудля этого требуется еще несколько хитростей. Один из вариантов, который у вас есть, - это добавить нужные операции в качестве параметров типа, а затем иметь функцию inline
, которая захватывает операции и передает их как обычные функции вашему типу VecFunc
. Вот пример:
type VecFunc<'T1, 'T2>(f:'T1 -> 'T2, mult:float * 'T1 -> 'T1, add:'T2 * 'T2 -> 'T2) =
member x.F = f
member x.Mult = mult
member x.Add = add
static member (.*) (a:float, f:VecFunc<_, _>) =
VecFunc((fun v -> f.F (f.Mult(a, v))), f.Mult, f.Add)
static member (+) (f1:VecFunc<_, _>, f2:VecFunc<_, _>) =
VecFunc((fun v -> f1.Add(f1.F v, f2.F v)), f1.Mult, f1.Add)
let inline vfunc (f:^V -> ^T) =
VecFunc< ^V, ^T>(f,
(fun (a, b) -> (^V : (static member (.*) : float * ^V -> ^V) (a, b))),
(fun (a, b) -> a + b))
let vf = vfunc (fun (v:Vec2) -> v + v)
average vf vf
Эта проверка типов, но я не совсем уверен, правильно ли она работает (я не уверен, что должны делать сложение и умножение векторных функций!) -но в любом случае это может помочь вам найти правильное направление.