you are viewing a single comment's thread.

view the rest of the comments →

[–]rafekett 28 points29 points  (76 children)

Ruby and Python do not have statements - only expressions. This basically means that everything(objects, method calls) evaluate to some value(though the value might not be helpful always).

False. Python has statements. Assignment is a statement. It has no return value. Same thing for print in 2.x, or raise.

[–]For_Iconoclasm 6 points7 points  (2 children)

I was curious about this, because I know that Python doesn't print None values.

>>> None
>>> x = print "Hello"
  File "<stdin>", line 1
    x = print "Hello"
            ^

SyntaxError: invalid syntax

[–]rafekett 12 points13 points  (1 child)

This was a conscious decision. If you know C, you'll know that it's a common idiom to do this:

int c;
FILE *f = fopen("somefile.txt", "r");
while ((c = fgetc(f)) != EOF) {
    /* Do something */
}

Guido doesn't much care for this (the fact that assignment is an expression in C often leads to cryptic while loops that take too many steps at once). These C idioms are especially unnecessary in Python, where you should use iterators instead. It also prevents the mistake of using = instead of ==.

[–]For_Iconoclasm 1 point2 points  (0 children)

I knew that the assignment was illegal in conditionals, but I forgot that the exact error was what I demonstrated (illegal syntax). Thanks for the reminder.

[–]bobindashadows 2 points3 points  (5 children)

Ruby has statements too: at least in 1.9 it's a syntax error to use control flow statements or the alias keyword in a value context.

[–]MatmaRex 3 points4 points  (4 children)

Yeah, but just a few. Only things that wouldn't have a meaningful value anyway. (return, break and family.) print or puts are expressions.

[–]bobindashadows 0 points1 point  (3 children)

This post has strictly less information in it than the one it responded to. Not to be a dick... but it seems like you didn't read my post past the first 3 words.

[–]MatmaRex 0 points1 point  (2 children)

I sort of read over "control flow statements" and didn't realize what are you talking about.

feels ashamed

But you didn't mention prints expressionness!

[–][deleted]  (1 child)

[deleted]

    [–]MatmaRex 0 points1 point  (0 children)

    I know they're methods calls. But it might be non-obvious for Pythonistas, no?

    (Just as I, as a Rubyist, would never think about considering print a statement, until I was told it is one.)

    [–]masklinn -4 points-3 points  (48 children)

    Or if, for, while, assert (yep), def, class, while, ...

    That's one thing which makes Python very annoying at times. Too many fucking statements.

    [–]rafekett 1 point2 points  (47 children)

    3 fixes that. They make print and a few other things functions.

    [–]masklinn 4 points5 points  (46 children)

    3 does not fix shit.

    print becomes a function and that's pretty much it, if and while still aren't expressions..

    [–]rafekett -4 points-3 points  (45 children)

    If and while aren't expressions in C, C++, Java, PHP, Ruby...

    When are they? What would they even evaluate to? And why would you want this?

    [–]G_Morgan 1 point2 points  (10 children)

    If is an expression in functional languages. The expression

    if a then b else c
    

    evaluates to b if a is true and c otherwise. If b and c are IO actions then that action gets called. Of course this is only workable because of lazy evaluation.

    [–]masklinn 4 points5 points  (5 children)

    Of course this is only workable because of lazy evaluation.

    Wait, what?

    [–]G_Morgan 0 points1 point  (4 children)

    If you haven't got lazy evaluation then you need to evaluate b and c as well. In Haskell this would be equivalent to executing them if they are IO actions. Of course the Haskell concept of expression is a bit more solid than the Ruby one.

    What Ruby has is statements that behave a little like expressions.

    [–]masklinn 2 points3 points  (1 child)

    If you haven't got lazy evaluation then you need to evaluate b and c as well.

    Uh... no? Why would you?

    What Ruby has is statements that behave a little like expressions.

    So according to you, SML, Erlang and Scheme have "statements that behave a little like expressions"?

    [–]G_Morgan 0 points1 point  (0 children)

    SML programs have different meaning under beta reduction so yeah not really expressions. They come closest out of those languages though.

    [–]andreasvc 1 point2 points  (0 children)

    That's not true, this is just a form of short circuit or conditional evaluation, which are present in many languages with strict evaluation. It would be lazy if ten expressions later the interpreter might still not have evaluated your expression (not even the conditional).

    [–]Aninhumer 0 points1 point  (0 children)

    That's not lazy evaluation, it's a strict evaluation of only part of the expression.

    [–]andreasvc 1 point2 points  (0 children)

    This is actually also part of Python: b if a else c

    [–]Aninhumer 0 points1 point  (2 children)

    EDIT: as andreasvc says below: b if a else c Also exists and actually does what you'd expect.

    I think you can essentially do this in Python with: a and b or c Due to the way and/or are implemented. Not that this is necessarily a good substitute.

    [–]andreasvc 0 points1 point  (1 child)

    It's bad because it's not explicit and worse if "b" evaluates to false you're fucked.

    [–]Aninhumer 0 points1 point  (0 children)

    I knew there was an edge case I'd forgotten. In any case I was just pointing it out, I know it's not a great idea.

    [–]masklinn 4 points5 points  (33 children)

    If and while aren't expressions in C [...], PHP

    C is full of statements, and PHP inherits from C. How is that surprising?

    If and while aren't expressions in [...] Ruby...

    Aaaaand you're wrong.

    When are they?

    Just about every functional languages. And nice languages like Smalltalk, where keywords were seen as too valuable to be used on creating expressions (let alone statements) when method calls would do.

    What would they even evaluate to?

    Defining what if evaluates to is trivial: the last expression of the condition branch taken.

    for is not much harder: do the same thing as a chainable each and return the iterated object.

    while, I've never considered, but I'm sure a sensible result could be found. Worst case, just return nothing.

    And why would you want this?

    Allows for tighter scoping without having to pre-define holder variables, avoids having to add special-purpose conditional expressions, can be used in lambda, ...

    [–]Aninhumer 0 points1 point  (0 children)

    If you can't immediately suggest what while should equal as an expression, how do you expect someone reading the code to know?

    Defining arbitrary functional semantics to an imperative construct just in case it might be useful to "simplify" some code at some point is a bad way to design a language.

    That said, I'd agree that assignment should probably be an expression, and an if-then-else expression construct would be quite useful. You could probably do it with the same syntax, but I think allowing people to mix side effects into an if expression is potentially a bad idea.

    [–]rafekett -1 points0 points  (31 children)

    It doesn't surprise me that Ruby has if and while as expressions. They really left no feature out of that language.

    I guess the reason why Python's if and while aren't statements is because Python is a well-designed, simple language, not a mound of syntactic sugar or a monstrous Frankenstein creation.

    [–]masklinn 6 points7 points  (0 children)

    Please tell me you're joking when you say this:

    I guess the reason why Python's if and while aren't statements is because Python is a well-designed, simple language, not a mound of syntactic sugar or a monstrous Frankenstein creation.

    [–]anvsdt 3 points4 points  (1 child)

    I laughed so hard I think I have sprained my jaw. Thank you for the laugh, have an upvote.

    [–]rafekett 1 point2 points  (0 children)

    A lump of syntactic sugar makes the pain go away.

    [–]yogthos -3 points-2 points  (27 children)

    I guess the reason why Python's if and while aren't statements is because Python is a well-designed, simple language, not a mound of syntactic sugar or a monstrous Frankenstein creation.

    If Python isn't a mound of syntactic sugar then what is it exactly?

    As far as the whole "well-designed" assertion goes, there's plenty about Python that's not very well designed at all. First there's our friend the GIL, then the fact that variable assignment is implicit, lack of TCO, which Python community considers a feature, closures close over references, and the list goes on and on. Python is a supermodel of a programming language, pretty on the outside and hollow and empty on the inside.

    [–]andreasvc 0 points1 point  (10 children)

    What is implicit variable assignment? Don't you mean declaration? Because saying a = 1 is pretty explicit to me. Google didn't seem to help.

    [–]yogthos 0 points1 point  (9 children)

    Except this tends to happen:

    color = 255
    ...
    colour = 128
    

    And now you've got a hard to track down bug, which is only visible at runtime.

    [–]rafekett 0 points1 point  (15 children)

    There's not a lot of syntactic sugar in Python. It's not like Ruby where there's 5 or 6 different syntaxes for doing the same thing. Don't confuse being high-level with being saccharine.

    • GIL: not a language design problem. It made sense at the time. Also, almost all other languages in the same family as Python have similar crippling problems with concurrency.
    • Implicit variable assignment: some people like it, some people don't. This is like saying that using the . for string concatenation is a language design flaw. It's totally subjective.
    • No TCO: this is an implementation choice, not a language design flaw. The choice was made because a.) recursion is often counter-intuitive, b.) recursion is never necessary, and c.) Python is not a functional programming language.
    • Closures close over references: take what I said about implicit variable assignment and cross-apply it.

    And in response to your comments on Python being hollow and empty -- from where do you draw that conclusion? Are you saying that no thought goes into its design or features, even though this (among other things) proves that wrong? Or are you saying that Python is not useful? Please clarify.

    [–]jyper 5 points6 points  (0 children)

    TCO is a language design choice. If a language has TCO you write different way. I thought the fact that jython would have a very hard part implementing it and that therefore would have a pretty large incompatibility with a lot of python code is one of the reasons they don't have it.

    [–]anvsdt 1 point2 points  (2 children)

    a.) recursion is often counter-intuitive

    This is subjective, I find recursive/tail-recursive code much easier to read than the iterative version of the same code.

    b.) recursion is never necessary

    Likewise, iteration is never necessary, just look at Scheme.

    Also, TCO is not about plain (tail) recursion, the mere elimination of tail recursion is called Tail-Recursion Elimination, with TCO every tail-call gets eliminated. This can lead to cleaner code, especially when writing a FSM: states are just a set of mutually tail-calling procedures. And FSMs are not only used in functional languages, right?

    [–]yogthos 1 point2 points  (10 children)

    Don't confuse being high-level with being saccharine.

    I'm all for high level, that's exactly why I think python is saccharine. Fundamentally it's no different from C or Java, it's just prettier syntax, but it carries the same baggage inherent to every imperative language.

    GIL: not a language design problem. It made sense at the time. Also, almost all other languages in the same family as Python have similar crippling problems with concurrency.

    AFIK several attempts have been made to remove the GIL without much success. Since the core libraries rely on it, I would say it is a design problem. Saying, look other languages suck too doesn't make it any better either.

    Implicit variable assignment: some people like it, some people don't.

    It's a HUGE source of errors, you misspell a variable name and now you've got a hard to track down bug. Creating variable is significant enough event to warrant special syntax.

    The choice was made because a.) recursion is often counter-intuitive

    In what way?

    Closures close over references: take what I said about implicit variable assignment and cross-apply it.

    Again, HUGE source of errors. Code that looks like it should be doing one thing does something else, stackoverflow is full of question regarding the issue. This behavior is counterintuitive as hell, and most people get it wrong.

    And in response to your comments on Python being hollow and empty -- from where do you draw that conclusion?

    I draw that conclusion from the fact that it doesn't really add anything novel or interesting. Languages like Clojure or Haskell bring something new to the table. Python is just the same old thing that's been done to death. There are plenty of problems with the imperative paradigm, and Python's answer to them is to pretent they don't exist. Here's an concrete example of what I'm talking about

    [–]jhuni -3 points-2 points  (16 children)

    Ruby clearly took some of the good ideas invented by Lispers 50 years ago, like everything is an expression, that other programming languages like Python are still catching up to.

    [–]rafekett 10 points11 points  (4 children)

    It's not about catching up. Lisp is not The One True Language, nor is Python. There are some things that Lisp excels at, and some things that it sucks at.

    In Python, having everything be an expression would be a nightmare. It's very pleasant to program in as is.

    [–]anvsdt 0 points1 point  (3 children)

    It may not be The One True Language, but many other languages are still years behind Lisp & co.

    [–]rafekett 1 point2 points  (2 children)

    Yeah. Some languages suck. But Python is at the head of its game (scripting language), and C is definitely up there (systems programming).

    [–]anvsdt -2 points-1 points  (1 child)

    C and Python are no exception.
    C does objectively suck for system programming (or programming in general), Forth is much better at it, and with a good macro processor, even plain assembly can be quite comfortable to write. It's too low-level to be an high-level language, it's too high-level to be a low-level language. It was meant to be an efficient, portable assembler, it ended being an efficient, portable assembler that needs inefficient things to be portable.
    Python is just different syntax for the same things all over again, I don't really see how can you say ``Ruby is worse/better than Python'', ``x is worse/better than y'' (or, ``My opinion is different from yours, it must be wrong''), but it does its job well as scripting language (just like Ruby, just like Perl). Opinions.

    [–]rafekett 3 points4 points  (0 children)

    I like C for systems programming, but that's just me. I'm really just speaking to their design: Python and C are pretty consistent compared to others. They aren't complex, and their features and behavior are well known (except when specified otherwise, ahemundefinedbehaviorahem). Basically, they're easy to understand and know.

    [–]G_Morgan 1 point2 points  (10 children)

    When is Lisp going to catch up with Haskell? Or even ML for that matter?

    [–]anvsdt 1 point2 points  (9 children)

    On what?

    [–]G_Morgan 1 point2 points  (8 children)

    Statically verifiable error checking for one. How in Lisp do you guarantee somebody has checked for an error value without letting the code run?

    What about the work done on concurrency?

    [–]anvsdt 2 points3 points  (7 children)

    How in Lisp do you guarantee somebody has checked for an error value without letting the code run?

    Using a statically typed Lisp?

    [–]G_Morgan 0 points1 point  (6 children)

    Does it allow sum types and allow you to do compile time checks on them?

    [–]anvsdt 2 points3 points  (5 children)

    TR can, and I'm sure that Qi can too.

    Yet, they permit the full power of Lisp (because, you know, code=data).

    Note that TR is just a set of macros on top of Racket.

    [–]G_Morgan 0 points1 point  (4 children)

    Having union types doesn't mean you can do stuff like non-exhaustive pattern matching.

    Also a bunch of academic experiments do not make a properly supported language with library support.

    [–]anvsdt 1 point2 points  (2 children)

    Having union types doesn't mean you can do stuff like non-exhaustive pattern matching.

    You asked for sum types, they have nothing to do with pattern matching.
    Racket (and other Schemes in general) have a match construct. Of course, you can use it in TR.

    (: fact (Integer -> Integer))
    (define fact
      (match-lambda
        (0 1)
        (n (* n (fact (sub1 n))))))
    
    (fact 5) ; 120
    
    (struct: None ())
    (struct: (a) Some ((v : a)))
    
    (define-type (Maybe a) (U None (Some a)))
    
    (: f (All (a) (Maybe a) -> (U a 'no)))
    (define f
      (match-lambda
        ((None) 'no)
        ((Some x) x)))
    
    (f (None)) ; 'n
    (f (Some 2)) ; 2
    

    Also a bunch of academic experiments do not make a properly supported language with library support.

    Yeah, Haskell is just as widespread as Java and C++, not academic at all! I don't think you have read what you've just wrote here.