Ich versuche, das frei Monade Muster anwenden, wie in F# for fun and profit beschriebenen Datenzugriff zu implementieren (für Microsoft Azure Table Storage)Freie Monad in F # mit generischen Ausgabetyp
Beispiel
Nehmen wir an, wir haben drei Datenbanktabellen und drei DAOs foo, Bar, Baz:
Foo Bar Baz
key | col key | col key | col
--------- --------- ---------
foo | 1 bar | 2 |
ich mit key = "foo" foo auswählen möchten und Bar mit key = "bar" ein Baz einfügen mit key = "baz" und col = 3
Select<Foo> ("foo", fun foo -> Done foo)
>>= (fun foo -> Select<Bar> ("bar", fun bar -> Done bar)
>>= (fun bar -> Insert<Baz> ((Baz ("baz", foo.col + bar.col), fun() -> Done()))))
In Interpreter-Funktion
Select
führt zu einem Funktionsaufruf, der einkey : string
und gibt einobj
Insert
Ergebnisse in einem Funktionsaufruf erfolgt, die einobj
nimmt undunit
Pro blem
I definiert zwei Operationen Select
und Insert
neben Done
die Berechnung zu beenden:
type StoreOp<'T> =
| Select of string * ('T -> StoreOp<'T>)
| Insert of 'T * (unit -> StoreOp<'T>)
| Done of 'T
Um Kette StoreOp die ich versuche, die richtige bind-Funktion zu implementieren:
let rec bindOp (f : 'T1 -> StoreOp<'T2>) (op : StoreOp<'T1>) : StoreOp<'T2> =
match op with
| Select (k, next) ->
Select (k, fun v -> bindOp f (next v))
| Insert (v, next) ->
Insert (v, fun() -> bindOp f (next()))
| Done t ->
f t
let (>>=) = bindOp
Allerdings warnt mich der f # -Compiler korrekt:
The type variable 'T1 has been constrained to be type 'T2
Für diese Implementierung von bindOp wird die Art in der gesamten Berechnung festgelegt, so statt:
Foo > Bar > unit
alles, was ich ausdrücken kann, ist:
Foo > Foo > Foo
Wie soll ich die Definition von StoreOp modifizieren und/oder bindOp, um mit verschiedenen Typen während der Berechnung zu arbeiten?
Ich kann Ihnen den genauen Grund für diesen Fehler in Ihrem 'bindOp' Code zeigen, aber der Grund dafür ist Ihr' StoreOp' Typ. Wenn Sie es genau betrachten, werden Sie sehen, dass es immer nur Operationsketten desselben Typs ausdrücken kann. –
Wäre es nicht möglich, alle diese Ebenen der Umleitung zu vermeiden und die einfachen CRUD-Sachen in etwas wie einem [Transaction Script] (https://martinfowler.com/eaaCatalog/transactionScript.html) zu tun? Das ist ähnlich dem, was Tomas Petricek im letzten Absatz seiner Antwort beschreibt (http://stackoverflow.com/a/41668459/467754). Siehe auch [Warum die kostenlose Monade nicht kostenlos ist] (https://www.youtube.com/watch?v=U0lK0hnbc4U). –
Die aktuelle Implementierung ist ein einfacher Satz von imperativen CRUD-Funktionen. Bitte beachten Sie den Kommentar zur Motivation. – dtornow