Yes, way in the heart of a functional program there's some code sorting lists of ints or something. But by that time, it's all labeled to the point where the functions just tell you what happens. As someone browsing the source, the nouns disappear.
list.stream().filter().mapToInt().sum() just reads better to me than sum( mapToInt( filter(list) ) )
def do(x, fs):
for f in fs:
x = f(x)
return x
Which allows left-to-right style programming do(list, filter, partial(map, int), sum)
I haven't put this into production code yet, but love pipelining scripts with doThey differentiate thread first/last for Fns with arity > 1, just like Clojure.
CompletableFuture<Member> asyncProjectMembers =
Async.pipeline(Accounts.where("id >= ?", 1000);,
(accounts) -> accounts.loadProjects("updated_at >= ?", Instant.now().minusDays(30)),
(projects) -> projects.loadMembers());
If you assign the intermediates to variables, each instance will have associated with it the related collection that's queried while avoiding N+1's. const people = list.stream();
const peopleOlderThan50 = people.filter(??);
const average = peopleOlderThan50.mapToInt().sum() / peopleOlderThan50.length;
Or some such thing. The more complex the chain the harder it is for me to read. Does anyone else have that experience?Also, for non-toy examples, there are real benefits to composing transformations, so that you don't pay the memory and performance costs of assigning intermediate sequences.
list |> map (1+) |> filter odd |> sumI haven't seen an autocomplete that works on pipes. But with a good type system, it is technically possible to list all compatible functions that take "noun" as first parameter.
Sometimes you supply partially applied functions (also with pipes in noun->verb order) and that's not so easy to match:
noun |> verb1 |> (otherNoun >> verb2) |> etc
But perhaps even functions that take "noun" as nth parameter could be suggested.
list map filter odd sumEnglish reads left to right overall, but writing instructions in English does not flow smoothly either way. e.g. "shuffle a deck of playing cards then deal four" reads more naturally than either "a deck of playing cards shuffle then four cards deal" or "deal four cards after shuffling a deck of playing cards".
shuffle(cards).deal(4) is a mix of both approaches.
Usually the languages either encourage cards.shuffle().deal(4) or deal(shuffle(cards),4).
In the 'noun-verb'/OOP version the sequence of modifications follows the english reading order where basically I only have to keep information about the last result in my head. The functional verb-noun version I have to pop in and out of layers: 'ok we're dealing what?, something shuffled, what are we shuffling?, the cards object/variable, ok we're dealing shuffled cards how many are we dealing? 4.'
In general the noun-verb follows the sequence of operations applied by the computer so it's easier to read.
But simply turning it around to cards.shuffle().deal(4) isn't a good answer, it's still the case that you can only put a small number of things together in a chain because it stops making sense. If the next move was to start the game, "cards.shuffle().deal(4).startgame('some-card-game')" does not make sense because starting the game is not something the cards do, but "start(deal(shuffle(cards), 4), 'some-card-game')" can make sense because start is a function which takes a board state and a game to start. It describes the world state, not the cards and their abilities.
That is, neither style is right, but a mixed style where both approaches are available and you can mix and match to be more expressive, works much better, IMO. Chunking a small number of things with prefix into one operation, or with postfix into one operation, but combining those chunks flexibly at a larger scale.
“Say that” “He said”
This isn't an argument for noun-verb over verb-noun, though it's an argument for more homogeneous syntax. `if value in dict` isn't noun-verb OR verb-noun.
There's not really a reason that f(<tab> couldn't complete with arguments (and it does in many systems). I think if you wanted to use autocomplete to make a point, you'd have to argue that it's more effective somehow to complete on verbs than on nouns.
``` (-> list parseInt isOdd sum) ```
Is the same as
``` (sum(isOdd(parseInt(list))) ```
Particularly useful if you're pulling a deeply nested key out.
On the other hand, tools like Hoogle [0] let you search for functions based on type, so you can search for e.g. `ByteString -> _` to find functions that take a bytestring. There's no reason the same paradigm can't be applied in an IDE.
(Note that this cant be a hash check because it’s not testing the presence of a key.)
(Also note that the story is different if you look at sufficiently archaic versions of Python.)
Ruby do have equivalents to Python magic methods... disguised as normal methods. For example Python `__hash__` is Ruby `hash` and both are used by built-in dict/hash values and you should be aware of that fact in both. There are some philosophical differences as well, Python likes to have standalone functions that are customized via magic methods (e.g. `str` vs. `__str__`, you don't normally call the latter) and in Ruby everything is method (e.g. `to_s`). It really seems like a matter of taste.
Auto-complete also really helps with discoverability. Consider checking for the presence of a key in a map/dictionary. In Java, auto-complete will quickly lead you to `map.containskey(key)`. In Python, though, you'd have to know that the syntax is `if value in dict`.
Now, let's check for the presence of a value. Auto-complete again quickly leads you to `map.containsValue(value)`. In Python, Google tells me that it's `if value in dict.values()`, which seems more difficult to stumble upon.
And that's for a built-in data structure. A lot of my job involves trudging through other people's code, trying to figure out how they architect their ball of cats. Auto-complete is a great tool for that; it lets you quickly and easily poke around, and see what the various nouns in the system can do.