Учитывая приложение ASP.NET MVC со следующими слоями:
- UI (Представления, CSS, Javascript и т.д.).
- Контроллеры
- Услуги (Содержит бизнес-логику и доступ к данным)
Причина отсутствия отдельного уровня доступа к данным заключается в том, что я использую поставщик типа SQL.
(Следующий код может не работать, поскольку это только черновик).
Теперь представьте себе службу с именем UserService, определенную как:
module UserService =
    let getAll memoize f =
        memoize(fun _ -> f)
    let tryGetByID id f memoize =
        memoize(fun _ -> f id)
    let add evict f name keyToEvict  =
        let result = f name
        evict keyToEvict
        result
И затем на моем уровне контроллеров у меня будет еще один модуль с именем UserImpl или его можно просто назвать UserMemCache:
module UserImpl =   
    let keyFor = MemCache.keyFor
    let inline memoize args = 
        MemCache.keyForCurrent args 
        |> CacheHelpers.memoize0 MemCache.tryGet MemCache.store
    let getAll = memoize [] |> UserService.getAll
    let tryGetByID id = memoize [id] |> UserService.tryGetByID id
    let add = 
        keyFor <@ getAll @> [id]  
        |> UserService.add MemCache.evict 
Использование этого будет выглядеть так:
type UserController() =
    inherit Controller()
    let ctx = dbSchema.GetDataContext()
    member x.GetAll() = UserImpl.getAll ctx.Users
    member x.UserNumberOne = UserImpl.tryGetByID ctx.Users 1
    member x.UserNumberTwo = UserImpl.tryGetByID ctx.Users 2
    member x.Add(name) = UserImpl.add ctx.Users name
Используя интерфейсы, мы будем иметь следующую реализацию:
type UserService(ICacheProvider cacheProvider, ITable<User> db) =
    member x.GetAll() = 
        cacheProvider.memoize(fun _ -> db |> List.ofSeq)
    member x.TryGetByID id = 
        cacheProvider.memoize(fun _ -> db |> Query.tryFirst <@ fun z -> z.ID = ID @>)
    member x.Add name = 
        let result = db.Add name
        cacheProvider.evict <@ x.GetAll() @> []
        result
И использование будет выглядеть примерно так:
type UserController(ICacheProvider cacheProvider) =
    inherit Controller()
    let ctx = dbSchema.GetDataContext()
    let userService = new UserService(cacheProvider, ctx.Users)
    member x.GetAll() = userService.GetAll()
    member x.UserNumberOne = userService.TryGetByID 1
    member x.UserNumberTwo = userService.TryGetByID 2
Очевидно, что в реализации интерфейса гораздо меньше кода, но он больше не похож на функциональный код. Если я начну использовать интерфейсы в своем веб-приложении, когда я узнаю, когда использовать функции более высокого порядка? - иначе я просто окажусь простым старым решением ООП.
Итак, вкратце: когда следует использовать интерфейсы и когда использовать функции более высокого порядка? - какая-то линия должна быть нарисована, или все это будут типы и интерфейсы, из которых исчезает красота FP.
