Please bring this discussion to the core mailing list. Before we make this work, we actually should decide if we want to make this work. We had similar discussion in the past and people were more inclined to a "no" because it relies on using variables to pass state/accumulate instead of explicitly accumulating it (which would be the more functional approach).
I would like to it to be more explicit too but I have never found a good syntax for it.
There are already ways to retain state between iterations of "for" comprehensions (eg, other processes, the process dictionary). I'm just suggesting that we make them prettier, more efficient, etc.
Elixir draws very few lines in the sand regarding how programmers should write their code. When it does so (eg, no mutable shared state), the reasons tend to be clear, objective, and pragmatic. Are there any such reasons in this case?
I'd rather not have the language making stylistic decisions for me. If I want that kind of "help", I know where to find it. Keeping state in loop variables may not always be the best idea, but I'd like to have the option.
I don't have strong feelings about the syntax. How about something like:
iex> i = 21
21
iex> for n <- (1..2), retain_state do
...> i = i + i
...> IO.puts i
...> end
42
84
[:ok, :ok]
-r
Could you provide an example where reduce wouldn't work? I'm just having trouble imagining a use case. I think functions in Enum might already solve this.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/B8127F7F-EA16-417A-B076-883E9051A4C5%40gmail.com.
I'd rather not have the language making stylistic decisions for me. If I want that kind of "help", I know where to find it. Keeping state in loop variables may not always be the best idea, but I'd like to have the option.
In many uses of recursion, a function saves some state in a data
structure, then passes the data structure to (another invocation
of) itself. What is the essential difference between passing a
map and retaining bindings on loop variables?
So, the Principle of Least Astonishment seems top be violated.
Worse, there is no way (other than managing state retention explicitly via
a reduce or somesuch) for the programmer to get the desired effect.
sum = 0for x <- [1, 2, 3], with: sum do
sum = sum + x
endsum #=> 6
This is imperative programming, we don't want this stuff. This is an enormous change from the language philosophy elixir has at present.I think there's value to providing reduction abilities to the for comprehension, but the whole idea of mutating external state ought to be anathema. If you want to count your "while" loops, spin up an agent and send it messages.
for x <- [1, 2, 3], with: sum = 0, do: sum + x
+1 Josh. I think it would be a good idea to avoid having two ways to use reduce without a good reason.
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAA1-O0w-V0NDdR3y5xWVztZMQppP2zccXKQcS0NKciv%2BcMkruA%40mail.gmail.com.
--
You received this message because you are subscribed to a topic in the Google Groups "elixir-lang-core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elixir-lang-core/_veg3CFQkS4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elixir-lang-co...@googlegroups.com.
What if we created "reduce comprehensions"? I don't like the proposed " with: sum" because it doesn't really fit the traditional reduce function signature as it returns the list rather than the accumulator. Reduce comprehensions would be just like reduce but would support multiple generators and filters like for comprehensions.
That would give all the flexibility in the world plus a great deal of convenience.
Example:
reduce x <- [1,2,3], y <- [2,3,4], x + y == 5, acc: sum, do:
sum + 1
end
We'd have to tinker with the account: part and provide an initial value, but I think it would be pretty useful.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAFOzVX_RFmF0K0aKMT6ZA8SbK9BBdTBJ1G3gVCG2TsJSAZsrSA%40mail.gmail.com.
I guess my hopes are:
My worry is someone will go from comprehensions to Enum and ask questions like "how do I carry state from one interaction of map to another?" and come to conclusions like "reduce is like a for comprehension when you use the 'with' keyword" (that would definitely make me feel sad inside...)
An alternate syntax might be something like (credit: José):
for x <- [1,2,3], reduce:
acc, do
acc +
x
end
Which is pretty similar to the 'with' keyword except we are calling it out under the correct technical name.
Wow... Botched formatting from mobile... Apologies... Will correct and resend from laptop when I get off this train.
The beauty of the current Stream and Enum implementations is the backbone of reduce and transform. I don't think we want to shelter the user from reduce and I don't think we should masquerade reduce as something else.
That's sort of what I see the discussion as. "Reduce is hard, so let's mess with other concepts that's are easier to grasp." By explicitly calling it a reduce there may be a learning curve, but reduce/fold/TCO are all non optional parts of a functional language.
I guess my hopes are:
1) don't overload terms (like for comprehensions) unnecessarily
2) Enum/Stream should mirror comprehensions whenever possible
My worry is someone will go from comprehensions to Enum and ask questions like "how do I carry state from one interaction of map to another?" and come to conclusions like "reduce is like a for comprehension when us you the 'with' keyword" (that would definitely make me feel sad inside...)
An alternate syntax might be something like (credit: José):
for x <- [1,2,3], reduce: acc, do
acc + x
end
Which is pretty similar to the 'with' keyword except we are returning one value and calling it a reduce (the correct technical name).
You received this message because you are subscribed to a topic in the Google Groups "elixir-lang-core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elixir-lang-core/_veg3CFQkS4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/9d70af37-e5a6-4ae6-bef8-ddc457d79b77%40googlegroups.com.
> c = :atom
:atom
# ⇣iterator ⇣iterator ⇣guard ⇣accumulator
> r = reduce a <- [1,2,3], b <- [4,5,6], a + b < 8, c = 0 do
c + a * b
end
45
> c
:atom
> r
45
reduce a <- [1,2,3], b <- [4,5,6], a + b < 8, into: [c, 0] do
c + a * b
end
reduce a <- [1,2,3], b <- [4,5,6], a + b < 8, acc: 0 do
@acc + a * b
end
Yeah I definitely understand the concern, my counter is:
The reduce keyword would make it obvious that the last "guard" passed is actually the accumulator.
The accumulator would naturally be a simple statement, possibly only `identifier = expression`, that always matched and had no effect on the iterators. (I'm not sure if "iterator" is the correct term, someone chime in if otherwise)
reduce n <- [1,2,3], into: [sum, 0] do
sum + n
end
reduce x <- [1,2,3], acc: 0 do
@acc + x
end
Op dinsdag 24 februari 2015 22:43:35 UTC+1 schreef greggreg:Yeah I definitely understand the concern, my counter is:
The reduce keyword would make it obvious that the last "guard" passed is actually the accumulator.
The accumulator would naturally be a simple statement, possibly only `identifier = expression`, that always matched and had no effect on the iterators. (I'm not sure if "iterator" is the correct term, someone chime in if otherwise)
My counter argument to this would be that it would make = look too much like assignment :)
Let me write down all reduce suggestions until now. To make it easier to compare, I let them all do the same operation:
Peter's suggestion
sum = 0
reduce n <- [1,2,3], acc: sum do
sum + n
end
Ben's suggestions
reduce n <- [1,2,3], acc: 0, as: sum do
sum + n
endreduce n <- [1,2,3], 0, acc: sum do
sum + n
end
Greggreg's suggestions
reduce n <- [1,2,3], sum = 0 do
sum + n
endreduce n <- [1,2,3], into: [sum, 0] do
sum + n
endreduce x <- [1,2,3], acc: 0 do
@acc + x
end
The difference in verbosity is pretty minimal in my opinion. Now that I look at all suggestions together, maybe Peter's original suggestion is best. It's still explicit, but it doesn't have a 'magic' connection between acc: value and as: variable.
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/de88b844-0207-4560-b25e-525736c15050%40googlegroups.com.
reduce n <- [1,2,3], acc: {sum, prod} = {0,0} do{sum + n, prod * n}end
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/516e699f-c9f5-4481-be72-ab5c032547a7%40googlegroups.com.
The win is when you have multiple collections and filters
reduce x <- [1,2,3, 4], y <- [3,4,5], x * y > 10, x * y < 14, acc: sum = 0 do
sum + x + y
end
Stream.map([1,2,3,4], fn(x) ->
Stream.map([3,4,5], &({x,&1})
end) |> Stream.filter(fn({x,y}) ->
x*y > 10 && x * y < 14
end) |> Enum.reduce(0, fn ({x,y}, sum) -> sum + x + y end)
Granted, with a Stream.cross function it would look like:
Stream.cross([1,2,3,4],[3,4,5])
|> Stream.filter(fn({x,y}) -> x*y > 10 && x * y < 14 end)
|> Enum.reduce
|> Enum.reduce(0, fn ({x,y}, sum) -> sum + x + y end)
Which isn't terrible. It still is a bit clumsy. Try adding a third dimension, z. In the reduce comprehension you add it to the comprehension and the block. In the Stream/Enum version you have to add it to every stage because now you are passing a 3-tuple instead of a two tuple.
The reduce comprehension syntax is fully macro-able:
iex(1)> quote do
...(1)> reduce x <- [1,2,3], y <- [3,4,5], n = 100, acc: sum = 10 do
...(1)> sum + x*y + n
...(1)> end
...(1)> end
{:reduce, [],
[{:<-, [], [{:x, [], Elixir}, [1, 2, 3]]},
{:<-, [], [{:y, [], Elixir}, [3, 4, 5]]}, {:=, [], [{:n, [], Elixir}, 100]},
[acc: {:=, [], [{:sum, [], Elixir}, 10]}],
[do: {:+, [context: Elixir, import: Kernel],
[{:+, [context: Elixir, import: Kernel],
[{:sum, [], Elixir},
{:*, [context: Elixir, import: Kernel],
[{:x, [], Elixir}, {:y, [], Elixir}]}]}, {:n, [], Elixir}]}]]}
iex(2)>
I would suggest that someone build it via a macro and use it for a bit before we try to get it into elixir-lang proper.
Any volunteers?
reduce n <- [1,2,3], acc: {sum, prod} = {0,0} do
{sum + n, prod * n}end
Is this really a win over Enum reduce?
Enum.reduce [1,2,3], {0,0}, fn n, {sum, prod} ->
{sum + n, prod * n}end
Oops! I meant to actually use the prod variable for the multiplication
reduce n <- [1,2,3], acc: {sum, prod} = {0,0} do
{sum + n, prod * n}end
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/516e699f-c9f5-4481-be72-ab5c032547a7%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/35A4877C-7300-49F7-8F44-5BF93B2D110A%40chrismccord.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/516e699f-c9f5-4481-be72-ab5c032547a7%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
You received this message because you are subscribed to a topic in the Google Groups "elixir-lang-core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elixir-lang-core/_veg3CFQkS4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAOMhEnyO42rTZsuhGKGoBP%3DtpxdY2q5z4PyQEZh8wXraBTqzyA%40mail.gmail.com.
With = you can just assign new variables. A better example would be:
iex(4)> for x <- [1,2,3], n = 4*x, y <- [3,n,5] do
...(4)> x + y
...(4)> end
[4, 5, 6, 5, 10, 7, 6, 15, 8]
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/d16b8c8c-7fc3-4aa4-af19-106da2431b9a%40googlegroups.com.
With = you can just assign new variables. A better example would be:
iex(4)> for x <- [1,2,3], n = 4*x, y <- [3,n,5] do
...(4)> x + y
...(4)> end[4, 5, 6, 5, 10, 7, 6, 15, 8]By defining n in the comprehension you we can use x in the definition and then use n in the collection that we unpack to y.Since the rest of the comprehension allows full pattern matching, I would expect the acc: to allow it too. Not sure how well this would work in practice.
On Wed, Feb 25, 2015, 9:08 AM Vincent Siliakus <zam...@gmail.com> wrote:
Op woensdag 25 februari 2015 00:41:07 UTC+1 schreef Peter Hamilton:
> I think I'm being misquoted. Let me correct :-)
Oops, sorry for the misquote.
>
> reduce n <- [1,2,3], m = 5 + 6, acc: sum = 0 do
> sum + n*m
> end
I like this syntax, especially if 'sum = 0' allows full pattern matching. The only thing I don't understand is the 'm = 5 + 6' line. Do you propose this to have special meaning, or is it just a filter that happens to evaluate to a truthy value?
- Vincent
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/c8304bf7-1466-4f45-8050-b354621c320c%40googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/c8304bf7-1466-4f45-8050-b354621c320c%40googlegroups.com.
With = you can just assign new variables. A better example would be:
iex(4)> for x <- [1,2,3], n = 4*x, y <- [3,n,5] do
...(4)> x + y
...(4)> end[4, 5, 6, 5, 10, 7, 6, 15, 8]By defining n in the comprehension you we can use x in the definition and then use n in the collection that we unpack to y.
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/61404e16-2731-478b-823a-d4eb4e3efefc%40googlegroups.com.
Second, unless I'm missing something I was unable to eliminate the `for` keyword of the comprehension. Elixir doesn't support varargs and the `for` keyword is actually a special case in the parser. I believe if varargs were supported then we would be able to implement both for and reduce comprehensions entirely in elixir. Perhaps that is a worthy topic for a new thread.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAOMhEnzYEcopudEDtCySssd9dxQEFw2PRTRuh6qfTJXCh7A-3A%40mail.gmail.com.