Как далеко я должен зайти ссылочной прозрачности?

Я создаю веб-сайт, используя erlang, mnesia и веб-машину. Большая часть прочитанной мной документации восхваляет достоинства ссылочно-прозрачных функций.

Проблема в том, что весь доступ к базе данных является внешним состоянием. Это означает, что любой метод, обращающийся к базе данных, больше не является прозрачным с точки зрения ссылок.

Допустим, у меня есть пользовательский объект в базе данных и некоторые функции, связанные с аутентификацией.

Ссылочно непрозрачные функции могут выглядеть так:

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.

Ссылочная прозрачность требует, чтобы я минимизировал количество ссылочно непрозрачного кода, поэтому вызывающая сторона должна получить объект из базы данных и передать его в качестве аргумента функции:

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.

Приведенный выше код, очевидно, не является производственным кодом — он составлен исключительно для иллюстративных целей.

Я не хочу втягиваться в догму. Оправдывают ли преимущества ссылочно-прозрачного кода (например, доказуемое модульное тестирование) менее дружественный интерфейс? Насколько далеко я должен зайти в погоне за ссылочной прозрачностью?


person Abtin Forouzandeh    schedule 20.11.2009    source источник
comment
Вы можете просто избавиться от всей базы данных, имея только функцию loop(MyWholeState) -> loop(receive X -> X end, MyWholeState) и loop(RecvHttp, MyWholeState) -> ... , loop(MyWholeNewState), сделав все ссылочно прозрачно.   -  person Zed    schedule 21.11.2009


Ответы (2)


Почему бы не использовать ссылочную прозрачность полностью?

Рассмотрим определение get_user_from_db. Откуда он знает, как общаться с базой данных? Очевидно, это предполагает некоторый (глобальный) контекст базы данных. Вы можете изменить эту функцию так, чтобы она возвращала функцию, которая принимает контекст базы данных в качестве аргумента. То, что у вас есть...

get_user_from_db :: userid -> User

Это ложь. Вы не можете перейти от идентификатора пользователя к пользователю. Вам нужно что-то еще: база данных.

get_user_from_db :: userid -> Database -> User

Теперь просто выделите это с помощью идентификатора пользователя, и, учитывая базу данных, через какое-то время функция предоставит вам пользователя. Конечно, в реальном мире Database будет дескриптором, объектом подключения к базе данных или чем-то еще. Для тестирования создайте фиктивную базу данных.

person Apocalisp    schedule 21.11.2009
comment
Теперь, когда я прочитал ваш ответ, он кажется совершенно очевидным. Но я бы никогда не подумал об этом - спасибо! Однако мне кажется, что было бы более разумно (в данном случае) написать функцию, которая возвращает функцию, имеющую контекст базы данных: get_user_from_db :: Database -> userid -> User. базовый контекст базы данных может измениться... - person Abtin Forouzandeh; 21.11.2009
comment
Вы можете получить одно от другого. a -> b -> c эквивалентно b -> a -> c. Все, что вам нужно, это функция более высокого порядка: flip f a b = f b a. Порядок аргументов выбор конструкции. В этом конкретном случае для компонуемости имеет смысл поместить базу данных в качестве последнего аргумента, поскольку это позволяет вам выполнять композицию Клейсли. - person Apocalisp; 21.11.2009
comment
Имея Database в качестве первого аргумента, функция может быть составлена ​​только из функций, возвращающих базы данных. Подозреваю, что таких не много. - person Apocalisp; 21.11.2009

Вы уже упомянули юнит-тестирование, продолжайте думать в этих терминах. Все, что вы считаете ценным при тестировании, должно быть абсолютно прозрачным, чтобы вы могли это протестировать.

Если у вас нет какой-либо сложной логики, которая может пойти не так, и единственный функциональный/интеграционный тест покажет, что она верна, то зачем идти дальше?

Вспомните YAGNI. Но там, где возможность модульного тестирования является реальной необходимостью.

person Christian    schedule 20.11.2009