2009-11-20 8 views
9

Estoy construyendo un sitio web usando erlang, mnesia y webmachine. La mayoría de la documentación que he leído elogia las virtudes de tener funciones referencialmente transparentes.¿Cuán lejos debería tomar la transparencia referencial?

El problema es que todo el acceso a la base de datos es de estado externo. Esto significa que cualquier método que llegue a la base de datos ya no es referencialmente transparente.

Digamos que tengo un objeto de usuario en una base de datos y algunas funciones que se ocupan de la autenticación.

funciones referencialmente opacos podría ser como:

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. 

referencialmente transparencia exige que puedo minimizar la cantidad de código referencialmente opaco, por lo que la persona que llama debe obtener el objeto de la base de datos y pasar esa como argumento a una función:

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. 

El código anterior obviamente no es un código de producción, está hecho solo con fines ilustrativos.

No quiero quedar atrapado en el dogma. ¿Los beneficios del código referencialmente transparente (como las pruebas probables de la unidad) justifican la interfaz menos amigable? ¿Qué tan lejos debo ir en la búsqueda de la transparencia referencial?

+0

Puede deshacerse de toda la base de datos, teniendo solo un bucle (MyWholeState) -> loop (recibir X -> X end, MyWholeState) y loop (RecvHttp, MyWholeState) -> ..., loop (MyWholeNewState) función, haciendo todo referencial transparente. – Zed

Respuesta

9

¿Por qué no tomar transparencia referencial por completo?

Considera la definición de get_user_from_db. ¿Cómo sabe cómo hablar con la base de datos? Obviamente, supone un contexto de base de datos (global). Puede cambiar esta función para que devuelva una función que tome como contexto el contexto de la base de datos. Lo que tiene es ...

get_user_from_db :: userid -> User 

Esto es una mentira. No puede pasar de un ID de usuario a un usuario. Necesitas algo más: una base de datos.

get_user_from_db :: userid -> Database -> User 

Ahora acaba de curry que con el ID de usuario y una base de datos dada en un momento posterior, la función le dará un usuario. Por supuesto, en el mundo real, Database será un identificador o un objeto de conexión de base de datos o lo que sea. Para las pruebas, dele una base de datos simulada.

+1

Ahora que he leído su respuesta, parece totalmente obvio. Pero nunca lo hubiera pensado, ¡gracias! Sin embargo, me parece que tendría más sentido (en esta circunstancia) a la función que devuelve una función que tiene una base de datos de contexto escribir: get_user_from_db :: Base de datos -> id de usuario -> Usuario supongo que esta versión potencialmente rompe la transparencia referencial ya que el contexto de la base de datos subyacente puede cambiar ... –

+1

Puede obtener una de la otra. 'a -> b -> c' es equivalente a' b -> a -> c'. Todo lo que necesita es una función de orden superior: 'flip f a b = f b a'. El orden de los argumentos es una elección de diseño. En este caso particular, tiene sentido que la capacidad de compilación coloque a Database como el último argumento, ya que eso le permite hacer la composición de Kleisli. – Apocalisp

+1

Al tener 'Database' como primer argumento, una función solo puede estar compuesta con funciones que devuelven bases de datos. Sospecho que no hay muchos de esos. – Apocalisp

3

Usted ya ha mencionado las pruebas unitarias, siga pensando en esos términos. Todo lo que encuentre valioso en las pruebas debe ser, en esencia, transparente para que pueda probarlo.

Si no tiene ninguna lógica compleja que podría salir mal, y una única prueba funcional/de integración vería que es correcta, entonces ¿para qué molestarse en recorrer la distancia extra?

Think YAGNI. Pero donde la capacidad de prueba de la unidad es una necesidad real.

Cuestiones relacionadas