Я снова и снова читал официальный документы по ограничениям типов, но я не могу понять, почему этот код не компилируется:
let inline transform<'A, 'a when 'A : (member Item : int -> float)> (a: 'A) : 'a =
a.[0]
ошибка FS0752: оператор «expr.[idx]» был использован для объекта неопределенного типа на основе информации, предшествующей этой программной точке. Рассмотрите возможность добавления дополнительных ограничений типа
И с :
let inline transform<'A, 'a when 'A : (member f : int -> float)> (a: 'A) : 'a =
a.f(0)
ошибка FS0072: Поиск объекта неопределенного типа на основе информации, предшествующей этому программному пункту. Аннотация типа может потребоваться до этой программной точки, чтобы ограничить тип объекта. Это может позволить разрешить поиск.
Очевидно, я не понял, как использовать ограничение члена с дженериками в f#. Общая проблема, с которой я сталкиваюсь, заключается в том, что я хочу сделать общие функции над «векторными» типами, такими как стандартные float[]
или Vector<float>
из MathNet.Numerics
или даже DV
из DiffSharp. Сейчас мне нужно получить специальную функцию для каждого типа, например (полный код):
#I ".paket/load"
#load "mathnet.numerics.fsharp.fsx"
#load "diffsharp.fsx"
open DiffSharp.AD.Float64
open MathNet.Numerics
open MathNet.Numerics.LinearAlgebra
open MathNet.Numerics.LinearAlgebra.Double
let l1 = 4.5
let l2 = 2.5
let a0 = [1.1; -0.9]
let inline transformVec (a:Vector<float>) =
let x1, y1 = l1 * cos a.[0], l1 * sin a.[0]
let x2, y2 = x1 + l2 * cos (a.[0] + a.[1]), y1 + l2 * sin (a.[0] + a.[1])
vector [x1; y1; x2; y2]
let inline transformDV (a:DV) =
let x1, y1 = l1 * cos a.[0], l1 * sin a.[0]
let x2, y2 = x1 + l2 * cos (a.[0] + a.[1]), y1 + l2 * sin (a.[0] + a.[1])
toDV [x1; y1; x2; y2]
Как видите, эти функции делают одно и то же, но работают с разными типами.
Я хотел бы получить общую функцию, например (не работающий код):
let inline transform<'A, 'a when 'A : (member Item : int -> 'a)> (toExt : 'a list -> 'A) (a: 'A) : 'A =
let x1, y1 = l1 * cos a.[0], l1 * sin a.[0]
let x2, y2 = x1 + l2 * cos (a.[0] + a.[1]), y1 + l2 * sin (a.[0] + a.[1])
toExt [x1; y1; x2; y2]
let transformVec = transform vector
let transformDV = transform toDV
Что мне не хватает?
Редактировать: у меня это наполовину работает с Mathnet.Numerics
let inline transform (toExt : 'a list -> 'A) (a: 'A) : 'A =
let inline get i : 'a = (^A : (member get_Item: int -> 'a) a,i)
let x1, y1 = l1 * cos (get(0)), l1 * sin (get(0))
let x2, y2 = x1 + l2 * cos (get(0) + get(1)), y1 + l2 * sin (get(0) + get(1))
toExt [x1; y1; x2; y2]
(transform vector) (vector a0)
Потому что он заставляет 'a
(предупреждение FS0064
) быть float
, чего я не хочу... (DV
из DiffSharp
возвращает тип D
для get_Item
, а не float
.)
замена декларации на
let inline transform<'a> (toExt : 'a list -> 'A) (a: 'A) : 'A =
заставляет компилятор хрипеть:
ошибка FS0001: здесь нельзя использовать объявленный параметр типа «a», поскольку параметр типа не может быть разрешен во время компиляции.