2016-06-21 20 views
5

Data la seguente tipo e funzione membroCome si fa un applicativo in F #?

type Result<'TSuccess, 'TError> = 
    | Success of 'TSuccess 
    | Error of 'TError list 
    with 
    member this.apply fn = 
     match (fn, this) with 
     | Success(f), Success(x) -> Success(f x) 
     | Error(e), Success(_) -> Error(e) 
     | Success(_), Error(e) -> Error(e) 
     | Error(e1), Error(e2) -> Error(List.concat [e1;e2]) 

e la seguente funzione inline

let inline (<*>) (f: ^A) (t:^A) = 
    let apply' = (^A : (member apply : ^A -> ^A) (t, f)) 
    apply' 

E questo sito chiamata

let y() = Success (fun x -> x + 1) <*> (Success 3) 

ottengo il seguente errore

let y() = Success (fun x -> x + 1) <*> (Success 3);; 
-----------^^^^^^^^^^^^^^^^^^^^^^^^ 
/Users/robkuz/stdin(473,12): error FS0001: Type constraint mismatch. 
The type 
    Result<'a,'c> 
is not compatible with type 
    Result<('a -> 'b),'c> 
The resulting type would be infinite when unifying ''a' and ''a -> 'b' 

Questa intera faccenda è un tentativo di emulare Haskells applicativo e la firma dovrebbe essere

(<*>) :: forall f a b. Apply f => f (a -> b) -> f a -> f b 

ma io non quanto ne sappia non c'è modo per esprimere che in F #

Tutte le idee su come fare che questo accada?

+0

La soluzione fornita da @Thomas è OK per il tuo caso, ma si noti che non funzionerà con tipi primitivi (elenco, array, opzione) e non funzionerà automaticamente con nessuna monade. Ecco una [soluzione più completa] (https://github.com/gmpl/FsControl/blob/master/FsControl.Core/Functor.fs#L133). – Gustavo

risposta

7

In generale, penso che non sia una buona idea provare ad emulare i pattern Haskell in F #. In Haskell, molti codici sono scritti in modo molto generico, poiché monadi e applicativi vengono utilizzati più frequentemente.

In F #, preferisco scrivere codice più specializzato, perché non è necessario scrivere codice polimorfico su monade o applicativi e rende semplicemente più facile vedere cosa sta succedendo. Quindi, non penso che vorrei mai scrivere l'operatore <*> che funziona in pratica su qualsiasi "applicativo" in F #.

Detto questo, il problema con il codice è che l'operatore <*> utilizza lo stesso parametro ^A tipo per entrambi gli argomenti e il risultato (mentre sono diversi tipi di chiamata) - se si utilizza tre parametri di tipo separati funziona benissimo :

let inline (<*>) (f: ^B) (t:^A) : ^C = 
    let apply' = (^A : (member apply : ^B -> ^C) (t, f)) 
    apply'