Ответ на этот вопрос будет отличаться для разных функциональных языков. F # не является чисто функциональным - он берет максимум от функциональных, императивных и объектно-ориентированных миров.
Для таких вещей, как ведение журнала и аутентификация, наиболее прагматичным подходом было бы использование интерфейсов (в F # это прекрасно подходит для использования интерфейсов, но люди обычно не используют наследование и предпочитают композицию).
Простой интерфейс имеет смысл, если у вас есть несколько различных функций, которые вы можете вызвать:
type IAuthentication =
abstract Authenticate : string * string -> bool
abstract ResetPassword : string * string -> void
Вы можете использовать object expressions, который является действительно хорошим способом реализации интерфейсов в F #. Если у вас есть только одну функцию (например, сообщения в лог), то вы можете параметризовать ваш код функции (который подобен интерфейсу с помощью только одного метода):
type Logger = string -> unit
Для таких вещей, как аутентификация и регистрация (вероятно, это не изменится во время работы приложения), вы можете использовать глобальное изменяемое значение. Хотя, если вы хотите синхронизировать запросы из нескольких потоков и есть какое-то изменяемое состояние, может быть хорошей идеей написать F# agent.