I prefer the Python code here. The import is explicit, so if you want to know where "date" comes from, that's easy. In a modern IDE I don't write imports manually anyway.
You could write it as a one-liner in Python as well, but I would prefer not to.
print("It's Saturday") if date.today().weekday() == 5 else None
That the Python library doesn't define constants for days is unfortunate, but not a limitation of the language.
The code is part of a standard, used-almost-everywhere library, `date`. Code in these libraries, from what I undersand, have a ton of thought behind them. It might follow that there were good real-life use-cases ready to use the code that influenced its presence in that library?
Ok, I'll bite. I don't, I think it was a knee-jerk reaction post that didn't think things through for more than a couple seconds. I'm guilty of many of these, it's not the end of the world.
I know many "scripts" that have far more curation, history, and accumulated effort than any "codebase" sensu your post, I don't think this has any relevance to his question. Use is use. "Real" or "Fake" in terms of a standard library like "date" (see my other post) just isn't a useful dichotomy IMO.
I'm not a fan of gatekeeping either, but on HN the proper etiquette is to give the poster the best possible reading of their post.
I just interpreted their post as talking about bigger, organized projects, instead of small scripts.
I personally draw a distinction between "codebases" and most everything else. One of the key differences is "would I start a new git repo?"
"Real projects" get a repo for sure.
It's not too diminish smaller projects. Almost all of the most useful things I've ever built were very small. But when you can grok the whole script, and you're likely to be the only user..."codebase" isn't the word I'd bust out.
I'm sure you can think of many exceptions to this. Thinking of exceptions is a good quality for programmers.
I don't think the word "real" (codebase) was a very important modifier.
Agreed, in complicated Ruby packages with layers and layers of dependencies it quickly becomes a nightmare to identify which class comes from which import, where it’s defined, etc. Never an issue in python
If I only used Ruby I'd probably prefer the Ruby example. As someone who jumps across many languages, I prefer the first. I found Ruby's approach to things was at many times too clever, to the point of becoming unreadable and cryptic. Python prefers clearness over cleverness.
I agree that's a little weird, but for me the consequence of it is small.
I'm going to catch that bug quickly.
When Ruby does a magic import, that would sometimes cost me a lot of time tracking it down when I first started with Ruby. I don't remember it being a big deal anymore after a couple of years.
I think the if being an infix operator is a bit "cryptic". I mean IMO it is easier to read - and in reality it's not a problem, because it's pretty unambiguous what's going on, but some people get annoyed by "oh you can do that in ruby?" Trigger that happens the first few times you see it.
Probably same with fields/methods with a postfix ?
> Probably same with fields/methods with a postfix ?
It's not a “postfix ?”, it's literally just “?” being part of the identifier name. (It's convention for methods that are boolean predicates.)
Also, Ruby doesn't have fields. (It has instance variables, but they don't have dotted access, so they aren't in the same namespace as methods of the same object the way fields are.)
It's a postfix sigil, even if it's part of the identifier name. You're not allowed to put ? in the middle of an identifier. (I use ruby and elixir, so this is second nature to me. It might not be for others).
I'm aware ruby doesn't have fields, I called it fields/methods because it's a method in ruby but people in other languages will read it as a field, which it is, a good percentage of the time.
I think every language I've used has annoyed me in how it handles dates and times.
I've been tempted several times to sit down and repeatedly read Dershowitz and Reingold's book "Calendrical Calculations" [1] cover to cover until I thoroughly understand it, and then write my own date and time library and port it to every language I use.
That would mean that when I learn a new language I'd have to take some time to port my date and time library, but looking at the languages I already know that would have been less than the time it took me to find and learn to deal with the quirks and annoyances in their libraries. I see no reason to believe that this will be different for any future languages I decide to learn.
Yes, I know this would be annoying to whoever has to take over my code when I'm gone, but I tend to stick around on projects until they reach end of life and so there almost never is anyone that comes after me.
I cordially invite you to look into the clusterfuck that is Go's treatment of date formatting. Everything else will seem clean and logical by comparison.
> Format returns a textual representation of the time value formatted according to layout, which defines the format by showing how the reference time, defined to be Mon Jan 2 15:04:05 -0700 MST 2006 would be displayed if it were the value; it serves as an example of the desired output. The same display rules will then be applied to the time value.
So, for instance, if you want to format a date in ISO format (YYYT-MM-DDTHH:mm:ss), you have to figure out how the reference date would be displayed in that format ("2006-01-02T15:04:05"), and pass that as the format string.
EDIT: Wait, I don't even understand how that can work, even in a broken way. Since I guessed 25000006060=9,000,000,000, I guessed that a "second duration" is really just a wrapper around `3,000` (ms) or `3,000,000` (ns) - but neither of those square to 9E9. In fact, the square root of 9E9 is a distinctly non-round number. What's going on here?
I can't fathom optional parenthesis. In this code it makes it very hard to know if today is an attribute or a method without knowing the context, meaning I can't scan the code, I have to read it carefully.
I'm going to emphasize this as well. Everything is an object, and everything called on an object is a method. Full stop.
Unlike with other languages, there is no ambiguity here.
In python, you don't know when you dir() an object whether your dealing with a property or method. In Ruby, you are guaranteed it's always a method. A perk is that you don't need parens.
Exactly. Once you get some basics of Ruby, there is no ambiguity, and I find it quite elegant.
And I think I’m in the minority here, but I prefer the cleanliness of optional punctuation.
That feature also allows for fluent DSLs (which seem to be falling out of favor for various reasons), but is one of the reasons I still love Ruby.
To look at the other side, I hate having to constantly add semicolons and parens in other languages. Makes my poor fingers hurt even more than they usually do.
I think this kind of falls in line with the idea of Ruby giving you sharp knives[0].
Like you I used to have a love / hate relationship with this because it feels inconsistent to sometimes use parenthesis and sometimes not. In a world where you just want to run Python's Black code formatter and always use parenthesis, having to think about when to use parenthesis feels really tedious.
But I think nowadays I'm happy that they are optional because when you want them to be optional it creates a really nice aesthetic to your code.
For example with Rails you can do: `2.days.ago` and then there's also things that feel like a mini DSL such as `validates :title, :body` or `belongs_to :users`. These just look nicer without parenthesis.
With that said, I do use parenthesis more often than not but it's nice to avoid using them when it makes something look nicer visually.
Given the context of this thread (comparison of ruby and python), it should be noted that python also has this. You can decorate a method with `@property` and call the method without parentheses.
It's really nice because it means you don't have to pre-wrap all of your properties with accessor methods. They can just be properties, and if something changes, you swap the property out with a decorated method and you don't need to change any code.
> I can't fathom optional parenthesis. In this code it makes it very hard to know if today is an attribute or a method without knowing the context.
Optional parentheses exist because it is impossible for there to be ambiguity: after a dot, if a name starts with a lowercase letter it is a method (and the reference is a call). If it starts with an upper case letter, it is constant (which is not callable as a method.) There are no other possibilities.
“Attributes” in Ruby are literally just getter methods (possibly paired with setter methods.) There is no object-dot-field access to fields (instance variables exist, but outside access to them doesn't use that syntax, but instead goes through a different set of method calls: Object#instance_variable-(get/set)
Makes reading ruby so much more difficult. Also optional return statements. If you need to refactor a function, it’s not always obvious that it’s relying on an implicit return and super easy to break things IMO
(not trying to convince you as I think in this thread no one will be convinced of anything)
Knowing that in Ruby a method will always return its last executed line unless explicitly returned early makes (at least for me) things very easy to get.
A method with two branches will always return the last line of the executed branch, thus you will always know what the response could be.
But this (IMO) is because Ruby OOP where the main concept is that when you are invoking a method you are actually sending a message to that object thus you should get a response. So Ruby makes this way of working with objects universal.
Once you call a method you will get a response.
I find it strange the other way around, working in languages where I need to put return even on the last line of the method. For example after almost one year of full time backend development in Ruby, I needed to write some JS code. I debugged for half an hour why a short function that I wrote was not working properly until I got it: I need to add 'return' to that last line to get the actual result. This was strange for me :)
In case you want (and need) to get how to think in Ruby I recommend you to listen to this talk by Sandi Metz (and in general I recommend all her talks even if they are old): https://www.youtube.com/watch?v=XXi_FBrZQiU
It presents OOP in Ruby in such a beautiful way and it takes a while to start thinking like this.
There may be other things about Ruby that I disagree with, but this is probably my primary disagreement.
It does make code more difficult to understand for exactly the reason you mentioned, and furthermore it can sometimes be disallowed based on the rest of the expression (such that parens are required).
In my own Ruby code, I often use parens when they are optional.
Although in this case it returns nil because it's implemented in C. But it would return the location if you overrode it.
There are various tools to see what the code is doing: invoking a debugger and stepping into the code, tracing the code execution via TracePoint, etc.
The Zeitwerk autoloader which comes with Rails enforces that the class name matches the filename.
Naming conventions, specs, a quick grep and avoiding generic names make it almost a non-issue in my experience - only happens every few years or so, and usually quite easy to fix.
Ruby gives you a lot of power, but it doesn't stop you from shooting yourself in the foot.
Python:
Ruby: