2

Given these 2 approaches:

Approach 1

module DomainCRUD =
   let getWhere collection cond = ...

module DomainService =
   let getByCustomerId f customerId = 
      f(fun z -> z.CustomerId = customerId) 

// USAGE:  
let customerDomains = DomainCRUD.getWhere collection 
   |> DomainService.getByCustomerId customerId

Approach 2

type DomainCRUD(collection) =
   member x.GetWhere cond = ...

type DomainService(CRUD) =
   member x.GetByCustomerId customerId =
      CRUD.GetWhere(fun z -> z.CustomerId = customerId)

// USAGE:
let domainService = new DomainService(new DomainCRUD(collection))
let customerDomains = _domainService.GetByCustomerId(customerId)

Which would fit functional programming the most? I assume approach 1 would, but also it feels a bit superfluous to call DomainCRUD.GetWhere collection each time.

Which would be the most flexible, and "readable"?

ebb
  • 9,297
  • 18
  • 72
  • 123
  • I'd say approach 1, if you're going to be using all those from F# only. If you want to see typical F# program structure, you can either check the [F# sources](https://github.com/fsharp/fsharp) or other [sources of awesome libraries like Deedle](https://github.com/BlueMountainCapital/Deedle). For example (in Deedle), there's [`Frame` data type](https://github.com/BlueMountainCapital/Deedle/blob/master/src/Deedle/Frame.fs) and [`FrameModule`](https://github.com/BlueMountainCapital/Deedle/blob/master/src/Deedle/FrameModule.fs) containing most operations on frames. – Patryk Ćwiek Dec 27 '13 at 15:07
  • Pretty much a duplicate of http://stackoverflow.com/questions/18458640/when-to-use-interfaces-and-when-to-use-higher-order-functions – Mauricio Scheffer Dec 27 '13 at 23:25

1 Answers1

3

Approach 1, for the following reasons:

  1. Functions associated with classes aren't curried but the ones associated with modules are. This means that you can partially apply a function inside a module to attain something that is typically done with a DI framework in OO code. (see Daniel's comment)
  2. You can omit the module qualification DomainCRUD.GetWhere by simply open DomainCrud.
  3. In addition to opening a module, you can mark it with [<AutoOpen>] or (conversely) [<RequireQualifiedAccess>] which provides additional flexibility not available with classes.
  4. The module based approach is less verbose.
eulerfx
  • 36,769
  • 7
  • 61
  • 83