3

I've seen this question but it doesn't help me because I'm writing functional code (Standard ML) and the answers there are heavily focused towards OOP (the OP is advised to use the Facade pattern, to use inheritance, etc -- answers that are not relevant to an SML programmer).

In two separate, largely unrelated modules, Foo and Bar, I need to accomplish the same task: given functions equal and equals with the signatures:

val equal: t * t -> bool
val equals: t list * t list -> bool

make two new functions with the following signatures:

val equal': t * t -> t option
val equals': t list * t list -> t list option

Here, t stands for either Foo.t or Bar.t. The natural solution is to use a higher order function:

local
  fun boolToOpt (comparison: ('a * 'a) -> bool) (left, right) =
    if comparison (left, right) then
      SOME left
    else
      NONE
in
  val equal' = boolToOpt equal
  val equals' = boolToOpt equals
end

I need to repeat exactly this code snippet in both modules.

Now, this is a tiny, minor implementation detail, and it would be a bad idea to let it be publicly visible from either Foo or Bar. At the same time, it seems too small -- and too tightly coupled to the two equal(s) functions -- to be factored out in to a module of its own. Maybe in another language, the equals' functionality would merit its own class, say, ComparisonDecorator, that overrides equal(s) to give me functions with the return type t option, but that's not an option here.

I run in to situations like these at least once per day. Is there a sane way to avoid just copy+pasting here?

  • 1
    What does "too small" mean in this context? Removing duplication is a clear (if minor) benefit - is there an associated cost that I'm not seeing? – Kilian Foth Sep 13 '14 at 8:23
  • @KilianFoth Maybe a better thing to say is: I think it's at the wrong level of abstraction to merit its own module. Saying "this makes a ('a * 'a) -> bool into a ('a * 'a) -> 'a option" doesn't have any semantic weight. And because of how the project is organized, in order to make a new module visible to both Foo and Bar, it will need to be visible to almost the entire codebase. A single function doesn't seem worth it. – Patrick Collins Sep 13 '14 at 9:16
  • 1
    I would favor a module (or several ones) of utility functions. – Basile Starynkevitch Sep 13 '14 at 10:04
  • You say a single function doesn't seem worth it, but what's the harm? Putting the function in a structure only adds like 2 lines of code. It's not leaking any important implementation details either. – Doval Sep 15 '14 at 2:43
5

If you have absolutely no idea of a good abstraction for a module, put the function in a generic utility module, at least temporarily. And after you found several utility functions, you will probably identify some of them "belonging together". That will give you a hint for a common module name for these group of functions, so you can finally move them from the utility module into a module with a better name.

Just be careful not to let your utility module grow arbitrarily - from time to time, you have to clean up things there, otherwise you will reach a point where you don't know what's in there and what's not.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.