4 ms·
The JS one does seem to have some more power than the Elixir one. For instance, in Elixir, if it's a bit kludgy to pipe to a second or third argument. I find th
by ollien 4y ago
The JS one does seem to have some more power than the Elixir one. For instance, in Elixir, if it's a bit kludgy to pipe to a second or third argument. I find this often when I want to insert into a map. You can always define more functions, but otherwise it's annoying, because you end up with something like
computation() |> &(Map.put(my_map, key, &1)).()
That said, with this less-power, you do kind of end up forced to design your functions in a way to where piping makes sense, and IMO it leads to cleaner and more consistent APIs.
- ch4s3 4y agoFrom what I understand the constraint is intentional to guide you in the direction of writing simple pipe chains.
- dmix 4y agoThat's probably a wise choice. Pipes are great for simple situations, mostly for code clarity. But it can be abused easily, like a hammer seeking nails. It's similar to await/async, you eventually start designing code that better suits that interface rather than pigeonholing it with complex syntax.
- spapas82 4y agoClojure has thread first ->, thread last -> and even thread as as->, to define where the pipe will be applied. I find it very cool!
- tytho 4y agoThere is another way that isn't _as_ kludgy, but still not as nice as the JavaScript proposal: computation() |> then(&Map.put(my_map, key, &1)) It's the big reason the `then/2` function was created from my understanding.
- lvass 4y agoIt's nice in Elixir because there's a strong convention that the first argument is where you'd almost always pipe into. The JS proposal is better for retrofitting in a language.
- ollien 4y agoOh wow, TIL about `Kernel.then/2`. That definitely works around the syntax problem. An aside, the implementation is kind of amusing. It almost seems unnecessary for this to be a macro but maybe the compiler can optimize this a bit more? I would expect TCO to simplify of my "simple" implementation of def then(value, fun) do fun.(value) end https://github.com/elixir-lang/elixir/blob/a64d42f5d3cb6c32752af9d3312897e8cd5bb7ec/lib/elixir/lib/kernel.ex#L2511 https://github.com/elixir-lang/elixir/blob/a64d42f5d3cb6c327...
- WastingMyTime89 4y agoPipes work really well with named arguments and partial-application. Both functionalities make it easy to get the unary function you want with minimal cruft. Lambdas solve the more complex case. In Ocaml, you often end up doing things like: List.create 0 3 |> List.map ~f:(fun x -> x+1) |> List.fold ~init:0 ~f:(fun acc x -> x+acc) |> Stdio.print_endline "%d" I'm ambivalent about adding them to JS however. It's a nice feature but I don't think it works well with the rest of the syntax.
- sph 4y agoAs I mentioned upthread, most Elixir functions are designed to have the thing they operate on as first argument. computation() |> &(Map.put(my_map, key, &1)) is terrible style when Map.put(my_map, key, computation()) works just as well and is more readable. It is pretty rare to have a pipeline that needs to insert the value elsewhere than the first position. And please, do not write single element pipelines, I see them far too often from Elixir beginners.
- ollien 4y agoI agree! I even pointed this out in my comment, but perhaps not clearly :) I think that the way it's done is a net-positive in designing cleaner APIs, but there are times when I've already done a pipeline, and storing the output is just the last step. This last step is just frustratingly, not always possible. I don't think one should do something like the above, it's just what you must resort to if you _did_ want to do it.
- sph 4y agoThere is nothing wrong with doing data = this |> is |> a |> pipeline save_to_file(file, data) Instead of trying to put the call to save_to_file into the pipeline by wrapping it in a closure.