Closed irigarae closed 9 months ago
This PR seems related to #63, but in the opposite direction.
I don't quite understand the purpose of this function, though. In the examples you give, the flip version is less concise. Do you have an example that would show the benefits of this function?
In general I've seen flip used as a function to switch the order of two arguments of a function (actually lodash is mentioned in the #63 that you mentioned, but I've seen it in haskell).
(defn flip [f y] (fn [x] (f x y)))
;; which can always be replaced as a lambda
(flip / 2) #_=> #(/ % 2)
;; it's complementary to partial in a way
(partial / 2) #_=> #(/ 2 %)
It can work whenever the function you are calling requires the variable argument in first position; if the argument was in last position you can always use (partial f ,,,)
. These are random examples, but as I say the function flip
can always be substituted by just a lambda, but it's the same for partial
, however I feel it's good to give it a name.
;; divide by
(mapv (flip / 3) (range 0 10 3)) #_=> [0 1 2 3]
;; substract
(mapv (flip - 3) (range 0 10 3)) #_=> [-3 0 3 6]
;; split
(mapv (flip str/split #",") ["a,b" "c,d,e,,," "f,g" "" "h"]) #_=> [["a" "b"] ["c" "d" "e"] ["f" "g"] [""] ["h"]]
However even if you still want to generate an operation that operates on a first argument, there are many functions that actually have more arguments, so being limited to 2 arguments doesn't make sense. It makes sense to have something like this in clojure:
(defn flip [f & args]
(fn [x] (apply f x args)))
;; we can use more arguments
(let [split-comma (flip str/split #"," -1)]
(split-comma "a,b,c,,,")) #_=> ["a" "b" "c" "" "" ""]
(let [ensure-orphan (flip assoc :parent :none)]
(mapv ensure-orphan [{:id 3 :parent 7} {:id 3 :parent 2}])) #_=> [{:id 3, :parent :none} {:id 3, :parent :none}]
(mapv (flip / 7 5) (range 6)) #_=> [0 1/35 2/35 3/35 4/35 1/7]
It's giving it a name to the concept of actually having a function that is only missing the first argument (which is usually the one that is modified for these types of functions), but indeed one could always just write a lambda, e.g., #(math/pow % 3)
instead of (flip math/pow 3)
.
Regarding #63, this suggestion is not intended specifically to play nice with these update-in
and family. However it's the missing piece together with comp
and partial
, this could be a way of writing the same thing (although as I say it's a side effect, not the main design idea):
(let [m {:users [{:id 1 :orders #{{:items [{:price 1} {:price 4} {:price 2}]}}}
{:id 2 :orders #{}}]}]
(= (->> (partial str "$")
(flip update :price)
(partial mapv)
(flip update :items)
(partial map)
(comp set)
(flip update :orders)
(partial mapv)
(update m :users))
{:users [{:id 1, :orders #{{:items [{:price "$1"} {:price "$4"} {:price "$2"}]}}}
{:id 2, :orders #{}}]}))
In any case, the suggested one was after looking at all the flexibility and default cases defined by partial
. But it could be simply ignored, since it might be confusing. I will simplify the given definition in the first post.
Because flip
can always be substituted for a more concise lambda, I don't think this is a function that would be suitable for inclusion in Medley. A function like #(/ % 2)
is also more understandable than (flip / 2)
, as the reader doesn't need to look up the definition of flip
.
There could be some benefits when lambdas are nested, but this seems enough of an edge condition that I don't think there's enough of an advantage to its inclusion in this library.
More concise is subjective, I think it goes in the same direction of giving names to specific operations, e.g., map filter reduce iterate cycle can all be done in a loop
in Clojure, but giving them distinctive names allows to identify on the spot and have a good mental model of what the code is going to do. Same with partial, comp, and the suggested flip.
I understand it’s a small function and it even takes more characters than having the lambda 😂 I close this issue since the intention was just to bring it up in case it seemed useful for the library.
Some function I've used for a while, and maybe it could be useful to have available in the library for everybody is
flip
: some function that takes extra arguments aspartial
but moves the argument to the first position. Inspired by the name in other languages. I've mainly used it to mix well some functions that lend themselves to->>
orpartial
with the ones that work with->
.If we generalize it we can accept multiple arguments also in the generated function instead of just one. Inspired by the source code of
clojure.core/partial
: the functionflip
can take many arguments and only rearrange the first one in front off
, while leaving the rest behaving as inpartial
. Suggested definition based onpartial
:Leaving it here for discussion (maybe this amount of flexibility is not needed, or the returned function should not accept 0-arity like partial, etc.). I can make a pull request if preferred.