I've been wanting to learn Rust for a long time, but I'm hit by the curse of Python being the second best at everything. Every time I try to write something in Rust, I think "okay, I can either spend a week learning Rust and writing this, by which point I might lose motivation, or I can have it done in Python in an hour".
Unfortunately, Python always wins, and it's also never not an option, since it runs on everything I care to develop on :(
Is it second best though? I can write an app in Go in the same amount of time it would take me in Python and it would be an order of magnitude faster, multithreaded, and type checked. Python is really quite slow and error prone if you think about it.
You're missing the point, he could've said brainfuck instead of python and the content would be the same: it's hard to motivate yourself to learn something new when you're already productive in another language and want to get work done.
guess what you've discovered the reason for ageism in the software industry. the way languages work nowadays is that it is easier to switch over to another language entirely that better solves your problem than create a new feature in [language you use].
if you can't do this quickly and easily, consider your career kaput when you hit 40
As someone in my 40's, I find far more of an insistence of sticking to a single language they're familiar with among younger developers.
Over the years, I've done commercial work in M68 asm, Awk, shell script, Pascal, C, C++, PHP, Javascript, WordBasic, Java, Ruby, and I've used about a dozen other languages on a hobby basis or out of a wish to learn.
But at the same time, I don't randomly switch languages for no good reason. I see the attitude of thinking it is easier to switch as a large part of the problem. Yes, there are cases where a switch is worthwhile. But all to often I see people describing how and why they switch in ways that makes it very clear that they didn't grasp the architectural reasons for why their original solution was broken.
The poster-child for that being Twitter, where they went from a Rails-setup that just violated pretty much every basic principle of proper architecture for scaling a messaging platform (I've written quite a bit about this, but the short of it is that their problem had been solved for decades: federate behind the scenes), and blamed it on Ruby and Rails.
It's perfectly possible they benefited from their language changes, but the main thing their language change gave them was that it forced them to re-design their system from scratch. And that is a common story. It may well be that blaming a certain platform is sometimes necessary to provide "political cover" to justify a large rewrite, but to me, large scale language changes often suggest covering up design flaws.
To bring things back to the article:
In this case it's certainly possible Rust is a good choice for them, especially as they had past experience with it and used it in some parts of the system already, but here, to me, this sentence already implies a design problem (lack of concurrency):
> The computations themselves are quick, but the problem is that we need to do a lot of them: for each order, we need to go over all available riders to determine which assignment would be the best.
Personally, my first step would have been to run the calculations in parallel. Maybe they'd stillalso want to switch it to Rust, but unless they stop adding riders, they've just deferred the real problem unless they took the opportunity of the switch to Rust to also parallelise the calculations.
Another issue here implies another area where a substantial portion of their time is a result of their design rather than language choice:
> This means 0.6 second is a Ruby/DB overhead of loading data and sending assignments to riders.
Why is there DB load here? There is simply no way that they are dealing with so many riders and locations in any given region that they can not hold an up-to-date view in-memory in the dispatcher (yes, they'll need to record it as well). A lot of developers these days are scared of loading things into memory, but you can often scale systems to 10x or 100x capacity by being willing to maintain in-memory state. Often you'll even end up with simpler systems.
This is not to suggest they're wrong to pick Rust or wrong to choose to switch, because that also depends on what they're experienced with and what works for them, but it's to point out that being quick to go for switching languages is often a result of different viewpoints rather than need. And sometimes lack of experience.
In some senses, this is what all the JVM languages are, or the WASM languages, or C FFI. You can build compatible layers between any languages, but at the end of the day, most people want to be able to read the libraries and other source code they’re working with.
Scala, Kotlin, and Java can all interop with each other, but at the end of the day much of the libraries get rewritten between the languages.
In addition to the sibling comments, this is also one of the nice things about the Clojure ecosystem: between the JVM and JS, you can target anything with an OS.
I would love to see that expand to have an LLVM/C target; the GC complicates that but it could be done.
I've done development in both and I doubt that you can be as "productive" in Go as in Python on a wide range of subjects. One reason is dynamic typing, the other is the vast amount of libraries the Python ecosystem offers. Nothing has been said about run time or robustness, though.
My issue with mypy is that I would like it to verify that I'm passing the right types into functions from my dependencies. For example, I do a lot of scripting with Python and boto3 to interact with AWS, and my scripts often fail the first time because I misread the field name or structure of a JSON object that goes into or comes from the API. It makes me wish I was using Java or Go, because at least then the compiler would have told me it was wrong.
I'm not sure if a solution to this exists yet. It seems like it's up to the dependency to adopt mypy as well?
There's also things like numba - basically a Python decorator that lazily JIT-compiles Python functions to LLVM, and supports numpy. For scientific work, it's quite often hard to beat (and sometimes somehow beats a raw c/c++ implementation of the same thing).
You should still consider learning some rust, because you'll learn some new concepts which might be a win for you anyway (thinking of error handling, options, enum matching, ownership, and what not). Trust me, learning rust zo some degree will make you a better programmer overall!
I'm already familiar with some of it and have written various things, but I'm not at the point where I reach for it and am proficient enough at it to prefer it. That's where I'd like to get.
Except that OCaml is still (for the foreseeable future) single-core only, just like Python, yet it doesn't have the ecosystem Python has. How many good database drivers are there for OCaml? How good are the web frameworks? If I wanted to write a Kafka consumer in OCaml, which battle-tested library would I pick? If I wanted to send an email through {MailGun, SendGrid, Postmarkapp}, are there any integrations that help with that?
Even an example of an echo server doesn't look that appealing: https://dev.realworldocaml.org/concurrent-programming.html#i... But, perhaps that is just unfamiliarity on my part. Rust was heavily influenced by some of OCaml's choices, but they chose to make the syntax more familiar and approachable to people coming from the mainstream programming world, although it does have its own quirks.
With Go and Rust as options, I'm not sure why someone would pick OCaml in 2019. In 2014, when that article was written, Go and Rust were both much less mature. I hear ReasonML/OCaml could be interesting for frontend work, but frontend is not my specialty.
Honestly, as easy as it is to hate on the npm ecosystem, Node.js can run circles around Python in performance, and TypeScript offers a pretty nice type system. If someone just wants a "better Python" for web-related stuff, TypeScript on the backend might be a good option, although I personally lean towards Rust and Go, depending on the project.
> I'm not sure why someone would pick OCaml in 2019
You definitely would if you already have millions of LoC in OCaml.. like Jane Street does.
For a random person, I would agree. I tried it myself for a few personal projects (I know Rust/Python/C++/Haskell reasonably well) and couldn't get myself to like it, subjectively.
Being 'single-core only' is not that much of a problem if you're coming from Python and immediately seeing a huge performance boost compiling to Go-style static native binaries, I think? People immediately jump on the 'single-core' thing with OCaml, but can you explain what exactly it is that you're trying to do with multicore that's not covered by a concurrency library like Lwt or a multi-processing library like Async Parallel?
Or maybe you can just take advantage of BuckleScript's high-fidelity approach to transpiling OCaml to JavaScript, and write some bindings to take advantage of basically everything in the npm ecosystem?
Sure, not every OCaml library is battle-tested, but I think that's not a good reason to immediately discount it. I didn't tell StavrosK to immediately commit to and rewrite everything in OCaml, I said he might find it interesting. Even with Go, Rust, and TypeScript as options, because:
- Not everyone is satisfied with Go's type system or its model of error handling, or its low ceiling for abstraction.
- Not everyone is satisfied with Rust's overall newness and potential compiler and library bugs, its lack of GC and the subsequent having to deal with borrow checking, or its slow compile times.
- Not everyone is satisfied with trying to model the dynamic complexity of JavaScript in a new, explicitly unsound type system. And it's no secret that the BuckleScript compiler can run rings around tsc for compile speed (not even mentioning the more powerful and battle-tested OCaml language).
Multicore is important because it allows as close to perfect resource utilization as you can get on each machine. When you spin up separate processes, you end up with separate connection pools to every other service (such as databases, mail providers, etc.) which is measurably wasteful, but more importantly, you never efficiently utilize your CPU. I've personally witnessed this with Ruby deployments. You can spin up a server per core, but then you always leave some small percent on the table. So then you could spin up more than one per core, but then you're introducing additional resource contention and scheduler overhead, not to mention even more connection pools. Each process also has its own separate garbage collector, which is less efficient than just having a single GC.
These are only a few things, and only discussing IO bound tasks. Once you start doing CPU intensive stuff, the problems are just made worse, especially if you want to spin off background jobs to work on things after finishing a request.
I know that a library of some kind has been written for each thing that I mentioned, but that doesn't make them ergonomic or reliable. The last time that I researched this, everyone discussing their experiences trying to use Postgres from OCaml had nothing good to say.
The TechEmpower benchmarks don't even include a single entry with OCaml, simply because no one has submitted one, which speaks volumes about the size and maturity of the state of writing a web server in OCaml.
For business critical functions like your database driver and your web server libraries, being battle tested is very important.
The Rust compiler is plenty mature, it's built on LLVM, and has had years of real world use by a growing number of companies. The Rust community places more emphasis on stability than any other I've been part of.
But, the async story and web server stories are not currently mature, which is why I'm happy to use Go for those things. Actix is as close to mature as those things get in Rust right now, and it's not up to my standards yet.
For someone to be so upset with all three of those languages on those counts that they decide to shoulder the burdens OCaml brings... that would be surprising. I had already pointed out that OCaml on the web has become a niche thanks to ReasonML (where BuckleScript is the compiler), especially since the web is predominantly single core.
"Explicitly unsound" is pretty funny to me. TypeScript has a really strong type system. To say otherwise as a negative thing at this point is to be purely pedantic in the most academic of ways, in my opinion.
I get why multicore in general is important and so do the OCaml folks, because they are currently engaged in a serious effort to land multicore in OCaml. That said, OCaml currently offers a great concurrency story (Lwt), and multiprocessing solutions that get you to like 90% of the way there when you really actually need multicore. You called me out for being pedantic about unsoundness which can lead to actual runtime errors, but aren't you being pedantic about insisting that OCaml is worthless if it can't squeeze out every single bit of processing power from your cores?
Discounting every OCaml library as not being 'ergonomic or reliable', really smacks of goalpost-moving. Every OCaml library needs to be immaculately designed and battle-tested? It's not even worth looking at otherwise? This is a weirdly high bar. No one even thinks like this about TypeScript, which by the way has library typings that are full of next-to-useless `any` and `function` types.
Agreed, the TechEmpower benchmarks don't include an OCaml entry, but (a) they're only relevant if you care about writing HTTP servers in OCaml; (b) they're only relevant if you care about benchmarks for HTTP servers in OCaml; and (c) the lack of a benchmark in itself doesn't prove anything about writing HTTP servers in OCaml.
The Rust compiler is an amazing effort, but it would be a mistake to equate its maturity level to the OCaml compiler, which has been an ongoing effort for more than two decades now. Frankly I find it strange that someone who immediately attack and dismiss OCaml when it's even suggested as worth checking out–and then accuse someone of being 'upset' with their suggested languages for writing a response.
> You called me out for being pedantic about unsoundness which can lead to actual runtime errors
"actual runtime errors" can and do happen even in Haskell and Ada. Unit and integration tests are important, no matter the language. TypeScript's type system is a huge step up from Python's, and it's better than Go's in many ways. TypeScript's biggest weakness for me is probably that it runs on Node.js, which is single-core.
As I have previously expressed, the major benefit to TypeScript is that it performs much better than Python while offering a strong type system and a strong ecosystem. The main things OCaml seems to offer are a stronger type system and better performance, when compared to Python. OCaml's type system might be marginally better than TypeScript's, but the Pareto principle might suggest that the extra 20% isn't worth the 80% more effort it will require to make up for OCaml's ecosystem. As I've also pointed out that I strongly prefer multi-core systems, you'll notice that I stated up front that I lean towards Rust and Go, not TypeScript. I just think TypeScript is a great fit for the OP, who seems to have been looking for a faster, safer Python (since they're so interested in Rust) with a drop in library for every task (so they can have it done in an hour). The most you have disagreed there is by saying that TypeScript's type system is slightly unsound. Okay. So, you can choose libraries which have really sound types so you can't easily misuse the library, but likely the libraries contain incorrect behaviors. Alternatively, you can choose libraries that are battle-tested by tons of major companies, but their types might not prevent you from every last possible misuse of the library. Either way, you need to write tests for your code to make sure the results are correct. In fact, you might need to write a lot of otherwise common libraries because OCaml is so niche. I don't see how OCaml wins there for the OP's use case, but I'm sure someone finds that trade off preferable.
> Discounting every OCaml library as not being 'ergonomic or reliable', really smacks of goalpost-moving
It's really not, because I made it clear up-front that I was asking about good and/or battle-tested libraries, not just a list of first hits on google. But it is easy to fall back on the defensive by making it sound like I was shifting things around. I literally asked "how good" are the web frameworks, "how many good" database drivers are there, questions which elicit discussion, not just links. Not "are there web frameworks" and "are there database drivers". I tried to be very clear about this up front.
> No one even thinks like this about TypeScript, which by the way has library typings that are full of next-to-useless `any` and `function` types.
I don't agree with your assessment here, since that really just seems like a strawman argument. `any` is a useful tool when used correctly; how else would you build a custom container like a Vec or a HashMap? It would be annoying to have a container that should be fully generic that can only hold some restricted set of types because the type system isn't powerful enough to allow it. However, it would be truly pointless for someone to build a nice TypeScript library using mainly functions that receive and return `any`, so it doesn't follow logically that experienced people would do that. Why wouldn't they just use JavaScript? This idea doesn't pass the smell test. Do you have any evidence for it? In point of fact, people write TypeScript definitions even for JavaScript libraries! This allows people to provide a nice interface into the JS code, so they don't have to see `any` everywhere they look. Why would they care more about the types in JS libraries, which are only approximate, if they're leaving all of their own code without type checking? TypeScript definitions for JS are only approximate, of course, but that's the real world. They're still helpful, just like documentation for a REST endpoint. When you make a REST call to an API, you aren't guaranteed that everything will work as documented, it might reject valid inputs, it might accept erroneous inputs, it might return values of the wrong types... TypeScript definitions for plain JavaScript libraries give you an opportunity to create a wall around your TypeScript code where the unsafety is easier to contain, but there will always be some level of unsafety in the real world. Eliminating 80% of it is a great start. The last 20% is not as easy as the first 20%, and the cost should be weighed against the benefit.
> and then accuse someone of being 'upset' with their suggested languages
I don't think you understood what I had written. I was saying that it's very unlikely that someone would look at all three of Go, Rust, and TypeScript and decide that the specific problems you mentioned are such dealbreakers that they would choose OCaml. I wasn't talking about you or me being upset, I was talking about any third party analyzing the trade-offs, but you seem to have taken it as some kind of attack against you? It definitely wasn't.
I don't hate OCaml. I have followed it for years, hoping it will progress in ways that I find important. Even years ago, multicore was right around the corner: https://news.ycombinator.com/item?id=9582980 but it's still not here, and it's still not in the foreseeable future.
> The Rust compiler is an amazing effort, but it would be a mistake to equate its maturity level to the OCaml compiler
Does it matter that OCaml's compiler has been under development for "more than two decades" if the amount of development is less than has happened on the Rust compiler in half the time? Rust, Go, and TypeScript have had staggering amounts of money and developer-hours dumped into both their compilers and ecosystems over the past few years, while OCaml's have barely moved at all, as far as I've been able to tell.
The uptick on OCaml seems to correspond to Facebook becoming interested, but on every metric (including total number of contributors, total number of commits, total additions, whatever), the Rust compiler has had a lot more developer-hours of attention than the OCaml compiler, which could be argued makes it more mature than the OCaml compiler... and this is just the Rust compiler frontend itself, which doesn't include the unbelievable amount of time and money that has been sunk into LLVM by Apple and others, which directly benefits Rust.
Let's just say that I don't agree with your conclusion about their relative maturity.
Let me reiterate: I do not hate OCaml. If anything, I'm annoyed with its stagnancy. It has so much potential, and I wish that potential would be realized. ReasonML is the best thing to happen to OCaml in the past 10 years, but that project has been so tightly focused on its objectives that it hasn't bolstered much of the surrounding ecosystem that would be desired for server-side initiatives.
I don't see how this comment thread can become productive, so I'm probably going to bow out now.
Yes, it is quite a lot faster (as well as being older and just generally more featureful after decades of refinements. For example, or-patterns in pattern matches).
It I understand correctly, Rust just got a limited form of or-patterns, with the requirement that they must be isomorphic (introduce the same number of bindings of the same type).
Same reason people use Electron for desktop apps. It's just more efficient in development time. And development resources are usually the most expensive part of development.
That's the "somebody else's problem" approach to determining requirements. Performance matters for the end user. They just might not be able to articulate it just like that.
And no one flew to the moon in a fighter jet. You only use the tool that can actually do the job. OP's point was that Python usually does the job, and using Rust out of the gate might be premature optimization I believe.
I think some languages you should learn because they make you a better programmer in general, even if you never use it for anything — rust and Haskell, in particular.
You might be more interested in switching to a language that is more similar to Python but has similar benefits to Rust, like Nim. The learning curve won't be as bad and you'll get fast and portable compiled binaries.
I thought the reason why "Python is 2nd best" isn't primarily its syntax, but the libraries which are available to use (and big enough communities using them).
e.g. with Python you can find libraries (and decent tutorials/examples) for: a small or big web framework, computer-vision, data-science / reproducible science, machine-learning, natural language processing, web-scraping or website testing.
I think python+pybind11 solves everything. Start with python and move slower parts to C++11 using pybind11. Bonus point: You can use all the existing C/C++ libraries.
https://github.com/pybind/pybind11
It's not that Python always wins but that it wins in the context of a work environment with deadlines and other constraints. If you make time outside of that environment for learning Rust, you can give yourself permission to develop new competencies without losing the productivity argument.
Key man risk is a real challenge. No doubt.
Python wasn't always so popular. There was a time when Java was safer for resourcing and Python was risky. What did managers do, if anything, to manage the risk that their sole Python programmer would one day likely leave?
It's a personal argument, I get frustrated and lose motivation when I have an idea I want to create and it takes way too long... Especially when I think "with python, it would have been done by now".
And this is the reason you shouldn't worry about jamming out your MVP in rails.
Because at the beginning you want to optimize for developer productivity and then when (if!) you get to a certain size, you can easily move to a solution that emphasizes machine productivity (aka performance).
I find that Rust is good on developer time, too, however it costs you all the developer time up front. In Ruby, Python or whatever you spend less time developing, but so much time debugging those little runtime errors that would have been compile time errors in other languages that I still think it only feels faster, it isn't really faster.
When you get to maintenance mode this effect becomes even more pronounced. Because it's much easier to not break something in a nicely typed codebase.
There's some languages that kind of bridge this gap between having a lot of compile time safety and still being easy to write. I'm thinking of F#, OCaml that have a lot of the same type safety, and barely need more type annotations than Python. They're not as fast as Rust, but also they don't necessitate thinking on the extra layer of ownership (which can cost you time).
People always think of the hours it takes to develop some feature and forget the days it takes to debug it.
I've spent many months trying to "easily move" an MVP over, after being saddled with a bunch of horribly structured data that has taken a massive amount of effort to unravel.
Devs need to stop thinking of it in such binary terms. You can get things out fast without pushing out crap. You can write organized code, built so it can be optimized if it ever needs to be, without it taking too much overhead. You don't need to swing the pendulum all the way to one side or the other.
Maybe Deliveroo should consider about removing their IP ban for this subdomain. Not everyone has a VPN readily availiable.
I'm traveling in Uruguay. I get:
Error 1009 Ray ID: 4a945b112e23c577 • 2019-02-15 02:31:00 UTC
Access denied
What happened?
The owner of this website (deliveroo.engineering) has banned the country or region your IP address is in (UY) from accessing this website.
I just moved some math heavy code from Ruby to Julia and was surprised how easy it was. By far the biggest difference was the 1-based indexing. Moving to Rust would taken me much more time, though in Julia it's harder for me to optimize the low level details.
Math/Science focused languages have usually used 1-based indexing, historically. Fortran, MATLAB, R, etc. In the primary fields where Julia is expected to be used, 0-based indexing would be a differentiator, not the other way around.
There seems to be a kind of Godwin's law for Julia that states that "When on an online discussion someone mentions Julia, the probability of a complaint about 1-based indexing is 1".
Or maybe it should be "When on an online discussion someone mentions Julia, the probability of that discussion turning into a 0-based versus 1-based indexing discussion is 1"
Then I realized that 90% of my mental gymnastics with indices got simplified. Explaining indexing to newbies is now immediate where before it took a slide, several examples, and a clever picture. Slicing is also more natural:
"1:3 picks out index 1, 2 and 3, which are the first, second and third element of the array."
Instead of the python version of
"0:4 picks out index 0, 1 and 2, which are the first, second and third element of the array."
> "0:4 picks out index 0, 1 and 2, which are the first, second and third element of the array."
I don't think that's right? It should pick out four things, not three.
Of course, you could argue that getting that wrong proves your point! I personally still prefer 0-indexing, but I definitely agree with you that having an exclusive upper bound is confusing.
Whereas 0 based indexing is based on convention from other languages where it arguably made more sense because it was just a convenience over pointer arithmetic. What reason other than convention is there for 0 based array indexing in any any language where an array isn't just a memory location and the index a calculation of type-size times index bytes?
Which is a valid argument for numbering starting at zero, but not just for programming languages, but in general and including human spoken languages. The common counter-argument is "People don't generally think that way unless trained to do so". Like many things in life, often convention and momentum often trump reasoned argument.
In general, much of what's chosen for language design is influenced to some degree by convention. This is a valid design choice, because you're maximizing use of existing domain knowledge of your audience.
In this specific case, I would argue that since in many languages where an array isn't a small shim over memory management (Ruby, Python, Perl, JS, etc) where easing user friendliness is at a premium over performance, 1 based array indexing would make better sense... if most people adopting those languages weren't already fairly well acquainted with 0 based indexing from CS programs or exposure to other languages (and it wasn't a simple concept to understand). Since many new users are familiar with it, and it is easy to understand, it's simple to just go with what some percentage of people already know, so convention wins out.
Maybe this is a stupid idea, but at this point the economy is tied to 0-based indexing so much, that I would rather get math moved to 0-based indexing, than computing to 1-based indexing.
Being able to easily port code between languages (and math articles) is important, when there's no clear winner, I like consistency.
Good luck with that. The 0-based indexing is non-existent outside of programming. No one who doesn't do pointer arithmetic would come up with it.
In mathematics 1...N is a perfectly good notation. Julia expresses the same idea with 1:N. This is consistent with spoken language (1 denotes the 1st element). Consistency, if anything, would not come down in favor of the software engineering choice here.
A lot of new very important papers in i.e. machine learning, quantitative finance come _after_ being implemented in computer code. I just looked at some, and they already seem to use 0 index for the initial states / base layer... here's just one example, where 0 and 1 based indexing is mixed, but I see it everywhere:
It's bad for me too, also missing some other features of Ruby (for example or, and keywords; ,,if'' keyword as a modifier, using blocks for passing a function as a parameter for functions), but there were some great surprises: the windows support seams superior to Ruby's, where I get a lot of compile errors; of course more/better math libraries.
There is no more correct. There is simply more to purpose.
0-based indexing won in programming because it is simply more to purpose in programming. We rarely operate on ranges, but we operate on offsets all the time.
And 0-based wasn't solely "C won so 0 won". We had 1-based languages for a LONG time, and, if they were sufficiently superior, they should have displaced C. They did not.
In addition, in proper programming languages you don't count--you iterate, fold, accumulate, etc.--and avoid the index altogether because it is error-prone.
1-based indexing causes all kinds of havoc in circular ranges. In particular when you try to access things in a circular manner (very common in programming--uncommon in mathematics), it causes grief.
// 1 based
new_index = index % N + 1
new_index = (index - 1) % N // Careful: the parentheses are REQUIRED
Actually what's funny here is that (index - 1) % N doesn't work in C, as it can be negative. You have to use (index+N-1) % N.
I still prefer 0 based indexing (for example for calculating the size of an array), but the worst thing is inconsistency between languages. 0 won, that's it.
(Side Note: For those reading this, the C operator isn't "broken", per se. There are three properties that modulo can adhere to but two of the three are mutually exclusive.)
C does not have a modulo operator. It has a remainder operator, which works perfectly fine. Calling C's `%` a 'broken modulo operator' is like calling `+` a 'broken minus operator'. Quite a few languages have separate operators, keywords, or functions for 'modulo' and 'remainder'.
> We rarely operate on ranges, but we operate on offsets all the time.
I would disagree with that assertion. Maybe it was true historically, but just look at how often ranges are used today - the fact that many languages have an abstraction for them in the core library is a testament to that.
I would also argue that having ranges (and underlying iterators) as opaque abstractions is preferable to conflating them with indices. Then you can have your cake and eat it too - the elements are counted naturally, but if you have an iterator to the first element, you can deal with 0-based offsets just as naturally.
Could also be that C is superior in other ways, there isn't really a big difference between 0 and 1 based indexing, and thus 0 based won on the back of Cs other strengths.
The Dijkstra discussion is only aesthetic preference, nothing more.
Edit:
In Julia the examples would also idiomatically be written in terms of the provided mod1 function:
> Could also be that C is superior in other ways, there isn't really a big difference between 0 and 1 based indexing, and thus 0 based won on the back of Cs other strengths.
Possibly, but then 1-based indexing certainly isn't enough of a positive to overcome the other stuff. And that's evidence, too.
> The Dijkstra discussion is only aesthetic preference, nothing more.
Dijkstra's comment says that people using the other 3 conventions were committing more errors--that's data.
> In Julia the examples would also idiomatically be written in terms of the provided mod1 function:
Agreed. The proper way is to encapsulate that behind a function so you don't have to think about it.
However, if you have to unpack that and repack it all the time (for example, Lua calling C), then you can't just encapsulate and forget about it.
That was a nice article but I would have liked to have seen how they compile/deployed the code. Was it in Gem form or a system lib or something else? Was it a separate package altogether or did it build in their CI? How hard was it to integrate into their deployments?
I guess I'm looking at it from a DevOps standpoint but those types of questions can (should!) inform the decisions you make about your code. Mixing in multiple languages (most likely) increases the footprint of your infrastructure and shouldn't be ignored. This is the same whether you use Rust, or NodeJS, or Python.
They just moved a computationally intensive task to a much faster language. Not as large scale as moving everything from Rails to Scala in the case of Twitter for instance, but pretty standard is any large Rails application to use faster languages for performance sensitive tasks.
Yeah, but some languages are designed with performance in mind and others are not. So while people have made faster implementations of JS, Python, Ruby, etc over time, they're at a disadvantage compared to languages like C/C++, Fortran and Rust. You can't really compete with a good compiler for those languages, which is why WebAssembly, Julia and Crystal exist.
I can't seem to find references for it right now, but there are severe advantages statically typed languages have over dynamically typed in terms of performance and weakly typed over strongly typed in terms of safety. The stronger the type system in a language, the greater the array of type level optimizations the compiler can do.
There is simply no way of knowing enough to optimize when compiling a python script comparing to rust for example.
Language depth has a cost though in terms of raw productivity, that is why there are languages like matlab that are there simply for prototyping and nobody in their right mind would use it for production purposes
There are languages which don't permit fast implementations. You might consider a "fast language" to be one for which it's possible to write a fast implementation.
Now you have me thinking about whether it would be possible to design a non-fast language--that is, a language resistant even to tracing JIT. Programs in this language would have to defy the usual pattern of having hot loops that can be compiled once and run repeatedly. That seems to imply that operators must change behavior as they're repeated. In the typical machine model that's only suitable for a toy language, but it might make sense for a language the somehow compiles to neural net logic...
I really like Crystal, but the lack of any large corporate sponsors or large production systems using it (that I’m aware of) means I would probably never choose it for anything besides toy projects.
But does Crystal have either of those? It's also a statically typed, compiled language. Rust would require a complete rewrite of the code, while Crystal would only be partial.
Crystal has meaningfully different syntax from Ruby in some important places:
* Strings cannot be single-quoted in Crystal. Single quotes are used for character literals, like C.
* The two languages differ substantially in how keyword arguments are specified. In Crystal, any argument can be specified by name as well as position.
* In Crystal a 'rocket-style' dict literal (i.e. `{ "foo" => "bar" }`) is a different type (Hash) from a 'keyword-style' dict literal (`{ foo: "bar" }`, a NamedTuple).
* Hashes are typed (inferred at the time of creation). You can't use keys and values of different types than what was in the hash when you created it. You _always_ have to specify a type for empty hashes, which means that any Ruby code that does `x = {}` and then stuffs things into `x` has to be rewritten.
* NamedTuples raise an error if you try to read or write a nonexistent key. Hashes raise an error if you try to read a nonexistent key.
* Crystal does not have singleton classes (`class << self`). The Module class doesn't exist. You can't execute code in class definitions (like constant initializations). Generally speaking, a lot of the very dynamic Smalltalk-y features Ruby devs take for granted are not present.
* `private`/`protected`/`public` are keywords, not methods, and they must be applied to each method individually (`private def...`).
There's a lot else. Now, a lot of these changes are good and fix issues that Ruby can't because of the need for backwards compatibility. This isn't a criticism of the language. The fact remains that Crystal's superficial similarity to Ruby hides a lot of substantial differences (a problem Elixir suffered from for a while as well). Translating simple code by hand is not difficult, but rewriting a larger project would be a huge undertaking.
> Hashes raise an error if you try to read a nonexistent key.
Depends on what method you use to read it. If you use [], what you describe is true, but if you use the nilable variant, []?, it is not. But yes, having to handle and think of nilability is certainly a big change that will affect a lot of code.
Having a runtime and GC is not unusual for a statically-typed, compiled language.
Crystal, in fact, has a runtime, that runtime has a GC, and the language FAQ notes that removing the GC would be impossible without complete redesign of the language.
I'd be worried of a false sense of similarity. Recently there was an article about crystal, some comments said Ruby was ~one inspiration and not the main goal. You could spend more time fighting your old habits with something that doesn't translate 1 to 1.
I spent some time working on a Crystal project and the superficial similarities to Ruby were more confusing than anything. Crystal isn't just "Ruby but fast and statically typed". They are quite different languages.
Just curious: why there are so many articles putting two things that varies a lot in their market positions in juxtaposition? e.g., Go or Rust/Ruby to rust/Wasm over JavaScript/NoSQL to MySQL
Because these are real decisions that real engineers have to make all the time. Sticking to Ruby for familiarity and ease vs rewriting in a less friendly language for the sake of speed, efficiency, and control is a real tradeoff, and it’s an important decision.
Given that, I wish we saw more of these sorts of stories. I have my own tales along similar lines for multiple tools in my own company. In my case Ruby/Python/Bash vs Go/Rust. This keeps coming up so I question your thesis here that these things aren’t comparables.
You get a greater benefit by combining things with two very different strengths. If something is too similar, it’s not worth the pain of a multi-language project.
It's a common scenario. You build something with tools that allow rapid development, then if you become successful need to find a path forward to scale.
There are many paths to do this - each with trade offs.
That's true, but according to my experience, it's usually better to perform gradual migrations like from Ruby to Crystal, if it can't solve the problem, try some other GC-based languages like Go. Things like Rust should be the last resort, especially when there is no Rust expert in your team.
EDIT: as some people explained below, this is certainly another trade-off. My experience is based on a case where the team was made of non-talented engineers and many of them stick to the same language in their whole career path.
To migrate gradually (and gain performance along the way) is exactly why they chose Rust. Go and Crystal have a runtime and thus can’t be easily cross-language called as they need to be run separately and communicate over network or other type of IPC, whereas it’s just a function call (and type conversion) in case of Rust FFI.
Meh, expertise can be cultivated internally with strong, open minded engineers. They won’t get it all right first thing but it’s just another tradeoff.
Crystal isn't exactly something I'd run in production.
Besides it's crippled as a language due to never allowing anything to be null, so there isn't any powerful metaprogramming you can do with it. That is a big turn off for me. Otherwise Crystal would be such a nice sweet spot IMO .
Not allowing NULL & having weak metaprogramming options sounds like advantages to me. you still have metaprogramming using language templates & scope shadowing, that's not at runtime but it's clearly good enough.
The problem with Crystal is more with the small community than the concept itself which is pretty good IMO.
They don't do it because runtime metaprogramming has a lot of disadvantages, it's difficult to optimize so it runs poorly, it throws away the static typing out of the window and it's difficult to debug. I spent hours debugging Rails code in the past because of the layers of monkey patching.
It also has a lot of advantages. It's as powerful as it is dangerous.
The real reason they don't do it is because they can't. It's inherently unsafe. In order to always be sure something is never null you have to be aware of what all it's inputs are so you can reason about it. That's not possible with runtime metaprogramming techniques.
I dunno I guess I just think it's an odd choice. Don't get me wrong, it's a neat experiment and I value it. I'm just slightly miffed that that particular experiment had to mixed in with what amounts to a statically typed language with the beauty and simplicity of Ruby but the friendliness and discoverability of static typing.
Uh, it allows stuff to be nil if you declare the type of something to include nil. But then you also have to handle it. In practice, it make my code better.
Because it usually involves doing a lot of things that are unsafe. Take reflection for instance. There is no way to guarantee at compile time that a string whose value is only known at run-time is actually a real method in the code that can be invoked.
To get the level of safety they have gone for they have to disallow anything that could potentially blow up.
This sounds very cool, and will be very useful when the whole Deliveroo service stops grinding to a halt every Friday night (yes, yes, I have an exciting life).
Harmless yes, but is it useful? Probably not. I have been teaching people Ruby and till this day, I have a hard time answering whenever a student asks me what the benefit of using Symbols over Strings is.
I know it was in place early on for memory / performance reasons but as of Ruby 2.2, there are zero-performance gains using symbols over strings. Unfortunately it's too late to deprecate them since it's a widely adopted practice.
When I teach Ruby, I try to evangelize how simple Ruby is but always hits the breaks whenever I have to explain Symbols.
And ultimately, no matter how much I try to ignore, I just can't stand how this looks:
opts = { adapter: :mysql}
It looks like a double wall of some sort which is fine for those who are used to it but not for those beginning. (Especially those coming from other languages like Python, PHP, and JS)
Symbols also cause occasional havoc when using 3rd-party libraries because they all have different preference of when to use it and when not to.
So IMO, there are no benefit of Symbols and it just creates confusion more than anything. And that is really the point I am making. I have a hard time reasoning with those PHP/Python folks why Symbol exists and it shouldn't really be that way.
But that isn't the reason symbols are useful. They signify intent as a constrained set of alternatives. Strings can take on any value, symbols should be limited to a set of things you know. Strings can be modified in-place, symbols are immutable.
`opts = { :adapter => :mysql }` is perfectly valid for scenarios where you are using symbol values. But the shorthand syntax for symbol keys was added later, and I agree is a bit ugly.
If you are generating symbols from input or from strings then they lose some value.
It is easy to explain the existence of symbols in Ruby especially if you are giving proper merit to the Smalltalk lineage. Erlang also has symbols for a lot of the same reasons and most would agree they are very useful there.
> as of Ruby 2.2, there are zero-performance gains using symbols over strings
As others have pointed out - this is demonstrably false.
# frozen_string_literal: true
a = ('fo' + 'o').to_sym
:foo == a
b = ('fo' + 'o').freeze
'foo' == b
The second comparison is a 1.5x slower than the former. This is because if a string comparison fails on identity, it then has to check characters. Symbols are always interned so can always stop as the identity comparison.
But I actually just like them as they convey intent - a symbol is a string I know about as the programmer. A string is a string that is runtime data.
Ok so I don't know about Ruby, but in Julia and elixir, symbols have O(1) comparison but more importantly they have key roles in abstract syntax tree manipulation; if you're doing any metaprogramming it makes sense that it exists.
Symbols in Ruby are basically concise strings, :id is a symbol, "id" a string. There are some differences of how they are allocated but it's mainly a style preference and a convention.
e.g. you would typically do { id: 1, name: "aboutruby" } where id and name are symbols, instead of { "id" => 1, "name" => "aboutruby" } with strings.
But really I don't see the point of removing the symbols.
This article was published before the Deliveroo team made full use of Rust. They acknowledged in the article that the project was a port of their logistics algorithms. They didn't refactor to idiomatic Rust nor have they made full use of concurrency, yet.
If the gains presented in the article are alluring, this is only the tip of the iceberg.
"It was clear that Ruby was a bottleneck here and we started looking at the alternatives." , how did you arrive at Ruby being the bottleneck ? not questioning your work here but curious to understand what part of Ruby was causing the performance issues.
Question is How a programming language becomes a bottleneck ? Also my understanding ( correct me if i am wrong here) Rust is more of a systems programming with low level access. Just trying to be curious with performance issues pointed out to a programming language ?
tl;dr important to know the engine behind the languages
This might not be the reason for the author, but for me I understood Ruby limitation with ruby global interpreter lock. For smaller applications I knew this wouldnt be an issue, but when my applications began slowing down I would check the processes and understand that my Rails application are operating only on one single thread. Then I access if I should keep try optimizing or if something like Elixir is a better solution. Same process with JS
so in which case, is it safe to say some languages( in this case Ruby) are not cut out for high performance at their root. And in which case we should not opt for such languages when we know we have to scale at some point. Is this right way to think about this ? moving to another language at the point when you have to scale is a costly affair to rewrite , isn't it ?
Even if the goal of getting Ruby 3 to be 3x faster than Ruby 2 on a typical Rails app is reached, it would be far from the 17x speedup they got by moving to Rust.
I guess it depends. TruffleRuby could be 200x faster in some benchmarks. So it is not that 17x speedup is an impossible task. It just needs some testing to be sure.
Obviously, performance is important; however, there is huge difference in the engineering cost of splitting your codebase into 2 languages and upgrading a language version in both the short term and long term. A 17x speed up in code doesn't guarantee a 17x speed up on future iterations of their product.
I am not saying they made the wrong choice; it seems like what they chose worked for their use case. It could have been the case that 2.6 didn't work for their needs, but I think it's still worth an investigation.
A naive perspective: Crystal is still a baby with a relatively tiny userbase, so I'm willing to bet there's plenty of Ruby gems that they use that don't exist in Crystal.
Can`t access it from Brazil...
```
The owner of this website (deliveroo.engineering) has banned the country or region your IP address is in (BR) from accessing this website.
```
https://i.imgur.com/TyDkmzc.png?1
You smartasses banned the entire country of Uruguay, where I reside, from reading this.
http://imgur.com/sdd6Mlv
If anything, this is a huge red flag indicating that I shouldn't care about anything Deliveroo does.
Don't be an asshole, don't geofence people for no real clear reason
I intentionally geofence most countries for certain initiatives where the high probability audience is in my country, U.S. (for cybersecurity reasons). Choosing a "deny" rule keeps out a lot of the crap one otherwise has to deal with (probes, scans, connections, DDOS, etc.
I see your point, but it isn't as bad as it may seem. Specifically I deal with B2B companies whose sales audience and customer base are solely the US (due to operational "boots on the ground" type of work)
Unfortunately, Python always wins, and it's also never not an option, since it runs on everything I care to develop on :(