-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
mini julep: if x then y #16389
Comments
The Perl/Ruby-style if/for modifier syntax seems like it would mix better with this. In other words: println("positive") if x > 0 # conditional execution
x^2 for x=1:100 # generator
[ x^2 for x=1:100 ] # comprehension
x^2 if x % 3 == 0 for x = 1:100 # filtered generator
[ x^2 if x % 3 == 0 for x = 1:100 ] # filtered comprehension It also has benefit of having precedent in other languages, which the |
Just a side note on issue management: #6823 discussed exactly this issue. At some point it was closed without comment, I followed up asking why it was closed because clearly there hadn't been a consensus to close it, but never got a reply. Seems it would be more efficient to not close issues like #6823, otherwise these discussions will just go in circles and all the points in the original issue will be repeated again. Would probably have made sense to change the title of #6823 at some point, and then add this here. |
Yes, good point. Indeed #6823 seems to be mid-discussion and probably should not have been closed. |
Maybe I'm in the minority since other languages have taken this approach, but I've always found the action coming before the condition to be really weird, e.g. I think of it like a conversation: Julia: "I'll print a string..." versus Julia: "If some condition is met, I'll print a string." |
@ararslan: I don't disagree, which is one of the reasons this feature isn't in Julia despite the prior art in Ruby and Perl. However, it does mix much better with the |
@StefanKarpinski Yeah agreed, it does mesh better with those. I'm still hopelessly attached to conditions preceding actions though. 😄 (But as I've found myself saying often recently, my opinion doesn't really matter; I'm just some guy.) I suppose if you had something like if x % 3 == 0 then x^2 for x = 1:100 then it isn't immediately clear that you're starting a comprehension/generator/thingamajig, because it also reads as though it can be grouped like if x % 3 == 0 then (x^2 for x = 1:100) i.e., if some condition then generator. I wouldn't mind if x % 3 == 0 x^2 end for x = 1:100 # More obvious what's happening, IMO
filter(x -> x % 3 == 0, [x^2 for x = 1:100]) # Verbose, but... ¯\_(ツ)_/¯ I guess if the condition were to follow the action in a generator, I think I'd prefer a different keyword than x^2 for x = 1:100 where x % 3 == 0 I think |
It seems nicer to me to have the data flow in a single direction, although in this case right to left rather than left to right. |
Can you elaborate on what you mean by that? |
The |
Things I don't like about
|
Oh yeah, I see what you mean now about the flow. That makes sense. Thanks for the explanation! Would it be weird to be able to do x^2 if x % 3 == 0 for x = 1:100 and not be able to do println("positive") if x > 0 ? |
All good points. I actually really like the syntax @StefanKarpinski first proposed in his first comment. I'm mainly interested in having conditional generators, with a more general short-form if-form as a bonus. |
Regarding generators, I was reading the In a sense, I feel that filtering is somehow a different operation to the conditional [(x^2 if i % 2 == 1) for i = 1:10] # [nothing, 4, nothing, 16, nothing, 36, nothing, 64, nothing, 100]
[x^2 (for i = 1:10 where i % 2 == 0)] # [4, 16, 36, 64, 100] Taking @ararslan's examples, if we allow
then IMHO it follows that
should probably emit Finally, if we were to adopt a shorthand As for tradition in Ruby/Perl, I feel its better to try and come up with the best solution rather than be bogged down by tradition. If it works and feels natural people will like it. |
Also, if we have # Make a circular array (filled with distance to center)
r = 5
[sqrt(x^2 + y^2) for x = -5:5, y = -5:5 where x^2 + y^2 <= r^2] This is kind-of cool and kind-of awful! |
Yes! I was about to post this same comment. Filtering operates on the iterator as a whole, and is not part of the computed expression inside. |
I also feel the same as @andyferris that the |
Obviously |
Personally, I'd rather be able to omit if length(A) != length(B) throw(ArgumentError("...")) If there is no
@StefanKarpinski I am totally on board with this. However...
I read I therefore do not think that having the "generator" be the starting point for reading it, is quite right, for a comprehesion. A for loop on the other hand... That makes more sense. |
I proposed this a long time ago but it did not get any traction: #1657. This would actually be a relatively non-disruptive change since it would be weird and rare to see some write code like this: if cond body
end |
What do you think about using |
Just a small side-comment: I don't see any way that |
@diegozea Same problem, I think. I still would prefer @carlobaldassi There are some things you could do to make it easier for humans to parse. You could force that single-statement ifs would not have a newline between the condition and the expression. Alternatively, you could force there be a newline after the expression if there was a newline after the condition; so, the following would not compile, but give a clear error message of course: if length(A) != length(B)
throw(ArgumentError("lengths must match")
if some_condition(A, B)
N *= 2 But, this would: if length(A) != length(B) throw(ArgumentError("lengths must match")
if some_condition(A, B) N *= 2 and this would: if length(A) != length(B)
throw(ArgumentError("lengths must match")
if some_condition(A, B)
N *= 2 This would also prevent you from making this kind of mistakes: if is_present(x)
y = 2 * x[]
return y * 2 |
I think the biggest argument for I do, however, recognize the potential confusion of the use of x^2 if x % 3 == 0 for x = 1:100 # filtered generator
[ x^2 if x % 3 == 0 for x = 1:100 ] # filtered comprehension is clearest, recognizing that the expression structure is
i.e. the I think the distinction is important here, because it's not that same logic that would allow println("positive") if x > 0 TL;DR: We should have |
@H-225 That seems weird and ultimately un-Julian to me, especially with the action on a new line with no It would still be a nightmare for an editor to parse because editors (like Vim) don't care what is and isn't a condition or action--indeed, they have no way of knowing without a Julia parser--but rather they care about the placement of things like Definitely 👎 for |
Is there a reason not to simply use the ternary question mark operator for the if-statement as well? You can already mimic an if-statement by doing
why not just make it so that if you don't include a colon then it's an if-statement, then you would just have
this way you don't have to introduce any new keywords, would this work? |
@esproff Funnily enough, I was going to suggest the opposite: extending the if-then idea in this julep to a single line if-then-else expression if cond then a else b where the else clause is optional. It might even lower identically to But of course, why can't we have all of these ideas simultaneously? |
@andyferris Yes that's the other, Pythonic, way to go. I guess it depends on how terse you want to be, mathematicians generally seem to prize terseness, but honestly I'd be happy with either, as long as I don't have to use the clunky
|
Indeed! |
The |
I got here from that discussion, but figured this was a better place to comment. I think that |
I agree with @EricForgy. Maybe I'm just used to them at this point, but to me, using C-style ternaries are pretty ubiquitous; I'd argue that it isn't just mathematicians who value this syntax. The only language I can think of offhand that has ternaries but doesn't support C-style is Python, and FWIW I despise Python ternaries. if condition
somelongaction1
else
somelongaction2
end anyway for readability. Regarding |
Personally, I much prefer I don't like to use IMO,
My order of preference: |
FWIW. I prefer the terseness and clear separation of the code with I do like to frequently do For ternary without the x==5 ? f1() :
x==6 ? f2() and one could imagine extending this to switch-type statements: match x:
5 ? f1() :
6 ? f2() In terms of readability, it depends. For many short conditions/actions it is much more readable (with clean spacing!!) to be able to line up conditions/actions on successive lines with clear separators. For generators, I would prefer x = [i^2 for i in 1:10 when i%2 == 0] I think it's more clear that this means |
I'll note that since discussing this, I would no longer object to |
Regarding the Perl/Ruby syntax "action if condition": I really like this syntax, and while I agree that it can reduce readability when used incorrectly, I also believe that it sometimes increases readability. For example:
After reading "throw domain error if" you already know that what follows is going to be a sanity check on the input. You can then skip to the next line if the details of the conditional are not important to you. The previous sentence is an example of how this construction frequently occurs in natural language. The action "skip to the next line" comes first, and the less important "if you're not interested" comes in a subordinate clause. Again, the reader might be able to guess approximately what will follow the word "if" without actually reading to the end of the sentence. (I will also give a self-referential example sentence, if I can think of one.) |
As an old Perl hand, I'm sympathetic to this syntax but I think it's going to be a hard sell to others who don't come from a Perl background. One argument in favor is that it matches the filter syntax in comprehensions. An argument against is that it's weird to have a construct where the evaluation order is not left-to-right. Of course, comprehensions have precisely that, so 🤷♂️ |
To me a related question is then: are we generalizing branching or filtering? This is a generalization of the branching (By the way, I'd love some nice syntax for filtering, like our nice f(x) for x in a # lazy map
f(x) for x in a if g(x) # lazy map - filter
x for x in a if g(x) # lazy filter where `f` is `identity`
f.(a) # map / broadcast
f.(a) if g.(a) # like a map/broadcast - filter operation
a if g.(a) # like the above where `f` is implicitly `identity`
[1,2,3] if [true, false, true] == [1, 3] # or something... here we simply make `if` an infix operator for filtering Sorry for going wildly off-topic and I'm not sure the above is a good idea at all, but just pointing out there might be another potential usage of the comprehension filtering Finally - I kind of agree with @tbreloff's observations above... binary |
@StefanKarpinski wrote:
println("positive") if x > 0 # conditional execution
x^2 for x=1:100 # generator
[ x^2 for x=1:100 ] # comprehension
x^2 if x % 3 == 0 for x = 1:100 # filtered generator
[ x^2 if x % 3 == 0 for x = 1:100 ] # filtered comprehension
And it would be better for the grammar, too. I think [(x^2 if x % 3 == 0) for x = 1:100] Then x^2 if x % 3 == 0 for x = 1:100 |
That would not be a filtered generator. |
Yes, you are right. Maybe it should be written like that: [for x = 1:100 x^2 if x % 3 == 0] Afaics, this would be valid parseable without the use of parenthesis, cool! |
Just thinking... [ for x = 1:100 if x % 3 == 0 push x^2]
for x = 1:100 if x % 3 == 0 push x^2 # other keyword could be used, e.g. yield This is more similar to the natural construct for x=1:100
if x%3==0
push!(somearray, x^2)
end
end |
I just saw this julep again while looking at something else. I still wish for something like the I also feel that the need to explain the |
|
I know what it does and I use it on occasion for brevity. But try as I might (as mentioned, for several years now) I can't see "a and b" as a sentence. It's just a mild cognitive dissonance every time. In contrast I've always found "a or b" to be fairly readable. |
Funnily, different aspects of this came up twice today at work. In the morning, we had a Julia PR which used In the afternoon, another software engineer (who doesn't know Julia) pointed out that, when speaking English with friends, sometimes he would answer the question of the form "Is it My stance on this issue all along is that using EDIT: thinking about this more, the problem here is entirely about side-effects and program flow. It's pretty easy for everyone to understand that "functional" usage of |
In some cases, I think "precedence" is clearer with
Also syntax highlighting in editors would help mark |
I have been aware of this discussion for all of an hour, so forgive me if this has been suggested. It seems to me like the short-circuit options of '&&' and '||' are used because we want simple if-statements in one line, without semi-colons. But short-circuiting seems like a fix that creates another problem: As a humam, it is hard to comprehend and parse that syntax without having seem it before, or knowing that the second expression is only evaluated when nessecary. The un-intuitive reading (as a human) seems to be due to both visual and logical imperfections with short-circuiting. It even seems that after knowing what it means, it can be harder-than-should-be-nessecary to read. If I am not mistaken, both problems could be fixed with a macro: @if condition result Negation could be handled with a ! before condition, or alternatively an @ifnot macro. It is just me, or is that free of ambiguity for the computer, easy to read for the human, and all in one line? |
^ I strongly agree with this.
You can already do the following, which looks nearly the same: if condition result end Below is a macro for the full if-then-else syntax. However, since syntax_error() = error("Valid syntax is either `@If cond Then ex` or `@If cond Then ex1 Else ex2`")
function If(exprs...)
n_args = length(exprs)
if n_args == 3
if exprs[2] != :Then
syntax_error()
end
ex = quote
if $(exprs[1])
$(exprs[3])
end
end
elseif n_args == 5
if ( exprs[2] != :Then ) || ( exprs[4] != :Else )
syntax_error()
end
ex = quote
if $(exprs[1])
$(exprs[3])
else
$(exprs[5])
end
end
else
syntax_error()
end
return esc(ex)
end
macro If(exprs...)
If(exprs...)
end
foo(x) = @If x > 0 Then println("greater than zero") Else println("less than zero") And here we can see julia> foo(3)
greater than zero
julia> foo(-3)
less than zero |
I would prefer something like
when would have less precedence than = and operated from right to left. |
I did not know that! I always though that one needed the semicolons where there is normally a newline. So in my case, the syntax if condition result end completely makes the use of if condition result_1 else result_2 end In this case, adding the keyword In reguards to the original discussion, I am in favor of adding "If x then y", even though I find it a bit reduntant with the one-line version of "if". But hey, write code however it makes sense to you, right? |
As someone posted a link to this discussion in Discourse
I have a soft spot for Julia: "I'll print a string."
|
In various discussions, it has been suggested to allow syntax like:
if x then y
As a short-form "if" statement and as an alternative to the common:
x && y
syntax which leverages the short-circuiting
&&
operator for conditionally executingy
(withy
often containing other side effects and not necessarily returning aBool
).The main advantages to this
if-then
construct being: more legible code, relying less on abusing&&
, and formally including an "if" statement form that doesn't require anend
keyword.It occurred to me the other day, that this syntax would also provide a convenient means for implementing #550, which would look like:
Relying on the fact that
if-then
doesn't require anend
keyword, which we would probably need in some form anyway even if we went with python-style guards:To be clear, the Julia guard syntax would essentially be doing a rewrite from:
to
Also as a clarifying note, this would be allowing the guard syntax at the generator level as opposed to just the comprehension level (which matches what python allows as well).
The text was updated successfully, but these errors were encountered: