2009-11-20 6 views
9

Ich erstelle eine Website mit Erlang, Mnesia und Webmachine. Die meisten Dokumente, die ich gelesen habe, loben die Vorzüge von referentiell transparenten Funktionen.Wie weit soll ich referentielle Transparenz nehmen?

Das Problem ist, alle Datenbankzugriff ist externer Zustand. Dies bedeutet, dass jede Methode, die die Datenbank trifft, nicht mehr referenziell transparent ist.

Sagen wir, ich habe ein Benutzerobjekt in einer Datenbank und einige Funktionen, die sich mit der Authentifizierung befassen.

referenziell undurchsichtige Funktionen könnten wie folgt aussehen:

handle_web_request(http_info) -> 
    is_authorized_user(http_info.userid), 
    ... 
%referentially opaque 
is_authorized_user(userid) -> 
    User = get_user_from_db(userid), 
    User.is_authorized. 

%referentially opaque 
lots_of_other_functions(that_are_similar) -> 
    db_access(), 
    foo. 

referenziell Transparenz erfordert, dass ich die Menge an referenziell undurchsichtigen Code zu minimieren, so dass der Anrufer muss das Objekt aus der Datenbank erhalten und dass ein als Argument übergibt in Funktion:

handle_web_request(http_info) -> 
    User = get_user(http_info.userid), 
    is_authorized_user(User), 
    ... 

%referentially opaque 
get_user(userid) -> 
    get_user_from_db(userid). 

%referentially transparent  
is_authorized(userobj) -> 
    userobj.is_authorized. 

%referentially transparent  
lots_of_other_functions(that_are_similar) -> 
    foo. 

Der obige Code ist offensichtlich nicht Produktionscode - es ist nur zur Veranschaulichung gemacht.

Ich möchte nicht in Dogma gesaugt werden. Nutzen die Vorteile von referenziell transparentem Code (wie der Test von beweisbaren Einheiten) die weniger benutzerfreundliche Schnittstelle? Wie weit sollte ich in Bezug auf die Transparenz der Referenz gehen?

+0

Sie können nur die gesamte Datenbank loswerden, nur eine Schleife (MyWholeState) -> Schleife (erhalten X -> X Ende, MyWholeState) und Schleife (RecvHttp, MyWholeState) -> ..., Schleife (MyWholeNewState) Funktion, alles referenziell transparent machen. – Zed

Antwort

9

Warum nicht referentielle Transparenz den ganzen Weg nehmen?

Betrachten Sie die Definition von get_user_from_db. Wie kann es mit der Datenbank sprechen? Offensichtlich wird ein (globaler) Datenbankkontext vorausgesetzt. Sie können diese Funktion ändern, sodass sie eine Funktion zurückgibt, die den Datenbankkontext als Argument verwendet. Was Sie haben, ist ...

get_user_from_db :: userid -> User 

Dies ist eine Lüge. Sie können nicht von einer Benutzer-ID zu einem Benutzer wechseln. Sie brauchen etwas anderes: eine Datenbank.

get_user_from_db :: userid -> Database -> User 

Jetzt nur, dass Curry mit der Benutzer-ID und eine Datenbank zu einem späteren Zeitpunkt gegeben, wird die Funktion, die Ihnen einen Benutzer geben. In der realen Welt wird Database natürlich ein Handle oder ein Datenbankverbindungsobjekt oder was auch immer sein. Zum Testen gib ihm eine Pseudo-Datenbank.

+1

Nun, da ich deine Antwort gelesen habe, scheint es völlig offensichtlich. Aber ich hätte nie darüber nachgedacht - danke! Allerdings scheint es mir, dass es mehr Sinn (in diesem Umstand) machen würde Funktion zu schreiben, die eine Funktion gibt, die eine Datenbankkontext hat: get_user_from_db :: Datenbank -> Benutzer-ID -> Benutzer Ich nehme diese Version zerstört möglicherweise die referenzielle Transparenz, da sich der zugrunde liegende Datenbankkontext ändern kann ... –

+1

Sie können einen von dem anderen erhalten. "a -> b -> c" ist äquivalent zu "b -> a -> c". Alles, was Sie brauchen, ist eine Funktion höherer Ordnung: 'flip f a b = f b a'. Die Reihenfolge der Argumente eine Design-Wahl. In diesem speziellen Fall macht es Sinn für die Kompostierbarkeit, die Datenbank als letztes Argument zu setzen, da man damit Kleisli-Kompositionen machen kann. – Apocalisp

+1

Wenn Sie 'Database' als erstes Argument verwenden, kann eine Funktion nur mit Funktionen erstellt werden, die Datenbanken zurückgeben. Ich vermute, dass es nicht viele davon gibt. – Apocalisp

3

Sie haben bereits Unit-Testing erwähnt, denken Sie weiter so. Alles, was Sie beim Testen für wertvoll halten, sollte transparent sein, damit Sie es testen können.

Wenn Sie keine komplexe Logik haben, die schief gehen könnte, und ein einzelner Funktions-/Integrationstest würde sehen, dass es korrekt ist, warum sollten Sie dann die zusätzliche Distanz gehen?

Denken YAGNI. Aber wo Unit-Testability ist ein echtes Bedürfnis.