2016-08-17 1 views
4

Auf meiner Suche nach besserem F # und einem besseren Verständnis der Funktionsweise von Suave.io habe ich versucht, einige wiederverwendbare Funktionen/Operatoren für das Erstellen von Funktionen zu erstellen . Ich verstehe, dass Suave tatsächlich seinen> => Operator implementiert, um speziell für asynchrone Optionen zu arbeiten, aber ich dachte, es würde mir Spaß machen, es zu verallgemeinern.Statisch aufgelöste Typen Member Constraints erkennen Erweiterungen von Systemtypen nicht

Der unten stehende Code ist von zu vielen Quellen zu kreditieren, und es funktioniert gut für Typen, die ich selbst definiere, aber ich kann es nicht für Systemtypen arbeiten lassen. Obwohl die Typenerweiterungen von Nullable und Option gut kompiliert werden, werden sie nicht als übereinstimmend mit der Memberbeschränkung in der Bindungsfunktion erkannt.

Als ich es versäumt habe, es für Option zu machen, hatte ich gehofft, dass es daran liegen könnte, dass Option in F # speziell ist, weshalb ich mit Nullable versuchte, aber leider keine Zigarre.

Die relevanten Fehler und Ausgabe von FSI ist in dem Code unten im Kommentar.

Jede Hilfe wäre willkommen.

Danke, John

open System 

let inline bind (f : ^f) (v : ^v) = 
    (^v : (static member doBind : ^f * ^v -> ^r)(f, v)) 
    // I'd prefer not having to use a tuple in doBind, but I've 
    // been unable to make multi arg member constraint work 

let inline (>=>) f g = f >> (bind g) 

// Example with Result 
type public Result<'a,'b> = 
    | Success of 'a 
    | Error of 'b 

type public Result<'a,'b> with 
    static member inline public doBind (f, v) = 
     match v with 
     | Success s -> f s 
     | Error e -> Error e 

let rF a = if a > 0 then Success a else Error "less than 0" 
let rG a = if a < 10 then Success a else Error "greater than 9" 

let rFG = rF >=> rG 
// val rFG : (int -> Result<int,string>) 

//> rFG 0;; 
//val it : Result<int,string> = Error "less than 0" 
//> rFG 1;; 
//val it : Result<int,string> = Success 1 
//> rFG 10;; 
//val it : Result<int,string> = Error "greater than 9" 
//> rFG 9;; 
//val it : Result<int,string> = Success 9 

// So it works as expected for Result 

// Example with Nullable 

type Nullable<'T when 'T: (new : unit -> 'T) and 'T: struct and 'T:> ValueType> with 
    static member inline public doBind (f, v: Nullable<'T>) = 
     if v.HasValue then f v.Value else Nullable() 

let nF a = if a > 0 then Nullable a else Nullable() 
let nG a = if a < 10 then Nullable a else Nullable() 
let nFG = nF >=> nG 
// error FS0001: The type 'Nullable<int>' does not support the operator 'doBind' 


type Core.Option<'T> with 
    static member inline doBind (f, v) = 
     match v with 
     | Some s -> f s 
     | None -> None 


let oF a = if a > 0 then Some a else None 
let oG a = if a < 10 then Some a else None 

let oFG = oF >=> oG 
// error FS0001: The type 'int option' does not support the operator 'doBind' 
+4

Erweiterungselemente werden für statisch aufgelöste Typabhängigkeiten nicht berücksichtigt. Nur kein Feature von F #, Punkt. [Bitte stimme über uservoice] ab (https://fslang.uservoice.com/forums/245727-f-language/suggestions/5664242-simulate-higher-kinded-polymorphism). –

+1

Wie von Fjodor erwähnt, wird dies nicht unterstützt. Während ich denke, dass dies eigentlich eine schöne Erweiterung zu F # wäre, bin ich ziemlich neugierig, was für Anwendungsfälle Sie dafür hatten? Ich finde Suave nett, weil es ziemlich einfach und nicht übermäßig abstrakt ist, also verallgemeinern die Operatoren nicht wie etwas, das zu besserem F # Code gehen würde - obwohl es Spaß macht, es einfach zu erforschen, natürlich. –

+0

@FyodorSoikin Danke, das erklärt es. Was für eine nervige Einschränkung, 3 Stimmen wurden abgegeben :) – JJJ

Antwort

3

Warum Erweiterungsmethoden nicht berücksichtigt werden in statischen Elemente Einschränkungen ist eine Frage, die viele Male und sicherlich wird es weiterhin aufgefordert, bis diese Funktion implementiert ist, in der gefragt wurde F # -Compiler.

Siehe this related question mit einem Link zu anderen verwandten Fragen und einem Link zu einer detaillierten Erklärung dessen, was im F # -Compiler getan werden muss, um diese Funktion zu unterstützen.

Jetzt für Ihren speziellen Fall löst die dort erwähnte Problemumgehung Ihr Problem und ist bereits in FsControl implementiert.

Hier ist der Code:

type Result<'a, 'b> = Choice<'a, 'b> 
let Success x :Result<'a, 'b> = Choice1Of2 x 
let Error x :Result<'a, 'b> = Choice2Of2 x 
let (|Success|Error|) = function Choice1Of2 x -> Success x | Choice2Of2 x -> Error x 

Und dann können Sie laufen:

#nowarn "3186" 
#r "FsControl.dll" 

open FsControl.Operators 

// Example with Result 
type public Result<'a,'b> = 
    | Success of 'a 
    | Error of 'b 

type public Result<'a,'b> with 
    static member Return v = Success v 
    static member Bind (v, f) = 
     match v with 
     | Success s -> f s 
     | Error e -> Error e 

let rF a = if a > 0 then Success a else Error "less than 0" 
let rG a = if a < 10 then Success a else Error "greater than 9" 

let rFG = rF >=> rG 
// val rFG : (int -> Result<int,string>) 

rFG 0 
//val it : Result<int,string> = Error "less than 0" 
rFG 1 
//val it : Result<int,string> = Success 1 
rFG 10 
//val it : Result<int,string> = Error "greater than 9" 
rFG 9 
//val it : Result<int,string> = Success 9 

// So it works as expected for Result 


// Example with Option 

let oF a = if a > 0 then Some a else None 
// val oF : a:int -> int option 

let oG a = if a < 10 then Some a else None 
// val oG : a:int -> int option 

let oFG = oF >=> oG 
// val oFG : (int -> int option) 

oFG 0 
// val it : int option = None 

oFG 1 
// val it : int option = Some 1 

Auf jeden Fall würde ich es so wäre der vorhandenen Choice statt Erfolg/Fehler oder implementing Success on top of Choice in Ihrem Fall empfehlen Ihre Beispiele, ohne irgendwelche Bindung oder Rückkehr zu schreiben.

Sie fragen sich vielleicht, warum für Nullable kein Beispiel gibt es, auch das ist einfach, weil Nullable kein Monade ist, es funktioniert nur auf Werttypen und eine Funktion ist kein Werttyp so haften besser auf Option für die gleiche Funktionalität.

+0

Vielen Dank für die ausführliche Antwort. Ich überprüfe die Links und insbesondere FsControl. – JJJ

+0

@JJJ In der Tat müssen Sie nicht einmal Erfolg auf Auswahl implementieren (siehe die aktualisierte Antwort). Trotzdem ist das sicherlich eine gute Idee, weil Sie alle vorhandenen Funktionen wiederverwenden und die Kompatibilität mit anderen Bibliotheken erhöhen. – Gustavo

Verwandte Themen