Marc Engher
Marc Engher

Reputation: 11

Expecting a type supporting the operator '-' but given a function type

Can anyone clarify why I got this message (see commented two lines below), and maybe point to the correct specification of user defined binary operators. Why do I have to define operator (.-->.) outside the type definition and and not (.>>=.)?

type ListA<'T> = NIL | Cons of 'T * ListA<'T> with
        static member cat (a:ListA<'T>) (b:ListA<'T>) :ListA<'T>=
            match a with
                | NIL -> b
                | Cons (a, tail) -> Cons (a, (ListA<'T>.cat tail b))
        static member join (a:ListA<ListA<'T>>)  =
            match a with
                | NIL -> NIL
                | Cons (a, b) -> ListA<'T>.cat a (ListA<'T>.join b)

        static member fmap (fn: 'T -> 'U) (a:ListA<'T>)  =
                 match a with
                    | NIL -> NIL
                    | Cons (a, tail) -> Cons ((fn a), (ListA<'T>.fmap fn tail))
        
        static member inline (.>>=.)(ma:ListA<'T> , k:'T -> ListA<'U>) =  ListA<'T>.fmap k ma |> ListA<_>.join

        static member inline (.<<<.)(mbc:'B -> ListA<'C> , mab:'T -> ListA<'B>) = (fun x -> (mab x) .>>=. mbc)
        static member inline (.>>>.)(mab:'T -> ListA<'B> , mbc:'B -> ListA<'C>) = (fun x -> (mab x) .>>=. mbc)
        
        static member join2 (a:ListA<_>)  =  a .>>=. id
    
    
    let (.-->.)(mab:'T -> ListA<'B>) (mbc:'B -> ListA<'C>) = (fun x -> (mab x) .>>=. mbc)
    let (.<--.)(mab:'B -> ListA<'C>) (mbc:'T -> ListA<'B>) = (fun x -> (mbc x) .>>=. mab)
    
    let f  = fun x ->  Cons (x, Cons ((x + 1), NIL))    
    let g  = fun x ->  Cons (x * 2, NIL)
    // NOT Compiling: let fgA = fun x -> (f .<<<. g) 
    // NOT Compiling: let fgB = fun x -> (f .>>>. g) 
    let fg = fun x -> (f x) .>>=. g
    let fg1 = (f .-->. g .-->. g)
    let fg2 = (f .<--. g .-->. f)
    printfn "%A" (fg1 3)

Upvotes: 1

Views: 100

Answers (1)

Fyodor Soikin
Fyodor Soikin

Reputation: 80734

Such are the rules of operator lookup. First (1) the compiler tries to see if the operator is defined as a method on one of the operands' types, and then (2) it tries to look in the current lexical context (i.e. the current module and any opened modules).

(incidentally, rule (1) is common for all .NET, while rule (2) is F# only)

.>>=. works, because it's defined on the type of its first argument (f x), so rule (1) applies.

.-->. works because it's defined in the current context, so rule (2) applies.

But in the expression f .>>>. g the type of both arguments is a function 'a -> ListA<'a>, and this type doesn't have operator .>>>. defined as a method, and no operator .>>>. is defined in the current context, so neither rule (1) nor rule (2) applies, and the compiler has no idea where to look up this operator.

Upvotes: 3

Related Questions