2016-06-03 2 views
3

Я хочу, чтобы понять код из this ответаКак работает неидиоматическая глобальная перегрузка операторов?

type Mult = Mult with 
    static member inline ($) (Mult, v1: 'a list) = fun (v2: 'b list) -> 
     v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b> 
    static member inline ($) (Mult, v1:'a  ) = fun (v2:'a) -> v1 * v2 :'a 

let inline (*) v1 v2 = (Mult $ v1) v2 

F # может разрешить перегруженные член. (Потому что он не поддерживает каррирование членов). Таким образом, я должен, он должен работать для методов, а

Но это не так:

type Mult = Mult with 
     static member inline Do (Mult, v1: 'a list) = fun (v2: 'b list) -> 
      v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b> 
     static member inline Do (Mult, v1:'a  ) = fun (v2:'a) -> v1 * v2 :'a 
    let inline (<.>) v1 v2 = (Mult.Do (Mult,v1)) v2 

Уникальная перегрузка для метода «Do» не может быть определена на основании информации типа до этот программный пункт. Аннотации типа могут быть . Кандидаты: статический член Mult.Do: Mult: Mult * v1:^a -> (^ a ->^a) когда^a: (статический член (*):^a *^a ->^a), статический член Mult.Do: Мульт: Мульт * v1: 'список -> (' список б -> ('а *' б) список)

синтаксис, в котором оператор $ определен сбивает с толку. Он принимает верхний идентификатор случая в качестве первого аргумента оператора и Visual Studio не жалуется на это

Mult выводится как тип Mult, но удивительно это не работает:

type Mult = Mult with 
    static member inline (!!) (mlt:Mult, v1: 'a list) = fun (v2: 'b list) -> 
     v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b> 
    static member inline (!!) (mlt:Mult, v1:'a  ) = fun (v2:'a) -> v1 * v2 :'a 

let inline (<!>) v1 v2 = (Mult !! v1) v2 

ошибки FS0003: Это значение не является функцией и не может быть применено.

ответ

6

Ваш второй пример не работает, потому что F # автоматически не устанавливает статические ограничения членов с помощью методов, как с операторами.

Так что да, это возможно, но вам придется писать ограничения вручную, компилятор не будет выводить их для вас:

type Mult = Mult with 
    static member inline Do (Mult, v1: 'a list) = fun (v2: 'b list) -> 
     v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b> 
    static member inline Do (Mult, v1:'a  ) = fun (v2:'a) -> v1 * v2 :'a 

let inline impl m v1 v2 = ((^T or ^a) : (static member Do:^T* ^a->(^b-> ^c)) (m,v1)) v2 
let inline (<.>) a b = impl Mult a b 

Верхнего корпус идентификатора, который Вы упомянули сопрягает размеченный Союз только один случай, поэтому он всегда будет успешным, и имя случая будет иметь одно и то же имя типа. Все это состоит в том, чтобы немного сократить количество кода, поскольку этот DU является фиктивным типом. Если это здесь запутанный пример с обычным классом:

type Mult() = class end with 
    static member inline ($) (_:Mult, v1: 'a list) = fun (v2: 'b list) -> 
     v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b> 
    static member inline ($) (_:Mult, v1:'a  ) = fun (v2:'a) -> v1 * v2 :'a 

let inline (*) v1 v2 = (Mult() $ v1) v2 

Вашего третий пример не работает, потому что (!!) является унарным оператором, не двоичным как ($)

Более подробной информации об этой старой технике в this old blog.

 Смежные вопросы

  • Нет связанных вопросов^_^