points by mlthoughts2018 6 years ago

Functional chaining (aka “fluent interfaces”) is very bad for dependency injection / mocking.

In f(g(x)) you can directly access or patch f and g as top level names.

In x.g().f() you have to patch methods internal to other structures, and this can lead to problems if the patching should only happen in a certain local scope.

I encountered this recently with differences between pathlib.Path and os in Python.

Consider

    def my_mkdir(pathname):
        pathlib.Path(pathname).mkdir()
        # or
        os.mkdir(pathname)

From the pov of testing and decomposition, the second option is much nicer, because I can patch os.mkdir directly, and not patch pathlib.Path.mkdir (and also worry about controlling when instance creation happens to use the patch when I need it, but let other possible pathlib.Path objects my tests interacts with be constructed normally). mkdir is even a very simple example since pathlib.Path.mkdir is an instance method but only relies on the string data the instance has. Imagine how much harder if pathlib.Path.mkdir has complex interaction with the internal object structure or other instance methods.

Obviously you _can_ solve it either way, but the fluent interface does nothing except require more code.

On this balance I think list comprehensions (or just fmap, which is all comprehensions are) are much, much better than chaining.

Also if you want to operate on data structures just operate on them with module functions.

I think pandas really messes up on this.

    agg(groupby(df, cols), funcs)

is way better than

    df.groupby(cols).agg(funcs)
eterps 6 years ago

A very valuable insight, thank you.