Throw away code
vorner.github.io
vorner.github.io
> This certainly is in part because I’m more proficient in Rust than in Python. It’s also because the Rust mental model is closer to how my brain works than the Python one.
You'll be more productive in writing the quick dirty hacks in language and programming model that you're the most comfortable with. That's it, the rest is IMHO just an attempt to rationalize your intuitive feelings.
That said I tend to prefer TypeScript over Rust for fast iteration, all else being equal, but I agree with the author that Rust can work quite well even for this usecase. And I would never pick Python for anything beyond 100 lines, if I'm being honest.
Like you, when approaching a new problem I like to nail down the terms we are discussing, literally scaffolding classes and types (in C++) as the problem is being discussed. It drives the adoption of unambiguous terms. Then as solutions are discussed the "ubiquitous language" helps clearly identify the interactions between types.
I could declare some type Foo, and that type might end up being:
- A class
- A record type
- A union type
- An intersection type
- A constant
or whatever else. But I can go ahead and start referring to that type in terms of function signatures, etc. Particularly for this kind of sketch-coding, it's really nice to be able to declare a concept and start talking about it before you even know what it is.
I understand that others' experiences differ.
was fun to show them that that "pseudo code" actually worked
It's funny, because I feel the same way, except I gravitate towards Clojure, where data is just data (and you stick it in vectors and hashmaps), and I don't feel the need for static types. At a later point, I may think about adding specs, but for quickly iterating at the REPL while I think about a problem, "plain data" works wonders.
With a dynamic language, you need a specific instance of data in order to write your functions, so you typically start from the top function and implement downwards. I think this works well when you have a light layer over some other library or system, but does not work well when you are implementing many layers in your own code.
I think this is what he hints at with:
> When I want to know if my code is working, I actually have to run the Python thing and feed it with data
I like REPLs, but Im experimenting with a type system + running a test on save that exercises the functions Im currently working on. This feels REPL-like as the iteration loop is fast.
It's interesting because I would previously implement something top down from the highest functions, but now I can implement mostly bottom up as the types/compiler gives me the language to define the layers.
Not bashing good types (I do like Haskell, too), just showing an alternative based on the same brainstorming idea the GP had mentioned.
I found this book very helpful in terms of thinking in a more functional type-driven way: https://www.manning.com/books/functional-and-reactive-domain.... It does eventually get into some deeper functional programming concepts but its still a nice read and easy to get into for anyone coming from more traditional object oriented coding.
These day's I've been using Bloop (https://scalacenter.github.io/bloop/) which makes Scala compilation super fast. It keeps a hot compiler running in a daemon and takes incremental compilation to a whole new level for Scala, truly a game changer for me.
Python has so many advantages for throwaway code with quick iteration. Duck typing. Easy string indexing. Bignum arithmetic by default. Trivial mutable globals. Default parameters. Trivial to make reference cycles. Access variables by name. Exceptions. And the big one is eval().
It didn't convince me, though. There were a lot of "this tool or library or feature exists in Rust", but no comparative advantage over other languages more compelling than this:
> This certainly is in part because I’m more proficient in Rust than in Python. It’s also because the Rust mental model is closer to how my brain works than the Python one.
I've been a python dev for about a decade.
I do use the REPL a couple times a week, but I wouldn't really call it "essential".
How do people use it that makes it so valuable?
Usually I just use it when trying to figure out how to parse some text or check what methods or properties are available on an object. Really nothing that I couldn't do in a test suite or the code itself.
Also, it's more important the more data-dependent your code is.
I can see how a REPL would be really useful when working with data, but for building out a webserver or desktop application, it doesn't have nearly as much value.
I am used to writing 2000 lines of code and having it run (mostly) correctly the first time. In C++, in my case. Otherwise my experience is like the author's. Types are not mainly for error checking. I demand they earn their keep as part of the solution, and they do.
But if you have to consume untyped input (such as parsing text), or produce untyped output (such as an image) then the value of a Notebook/REPL really shines.
I've never written code for which I've found the REPL that useful so either my domain is not conducive to REPL usage or I need a good tutorial or example to see what I missing and how to change my approach.
Any suggested videos or articles? though i suspect a video would be better for this type of thing
Someone also said the REPL, and while the REPL in Python is not as powerful as, i.e.: Clojure, it is still something that feels essential for a exploratory workflow that is the scripts case. Testing is not the same, it is much slower to interact, specially when you just want to check a hypothesis (also, most scripts I don't want to write tests, they will probably break anyway in a few months like the example of web scrapping I said above).
Check out IPython if you haven't, the REPL is a game changer.
- Hints and completions behind the Tab key
- Syntax highlighting as you type, along with auto-indentation
- Dynamic introspection with "?" and "??"
- Being able to run shell commands with "!" and capturing their output with "!!"
- Magic commands behind "%" and "%%"
- File system navigation
So, AFAIK rust will generate a standalone exe, python, out of the box, will not so I want to give someone else my throw away solution, not requiring them to install python to use it might be a win?
> Over the years, I’ve used Perl a lot (that one doesn’t care if it’s int or string… no, correction, in Perl everything is a string, ints just don’t exist. Well, kind of). It’s probably the language designed for throw away coding. I’ve done some Python too (that’s like Perl, but with proper objects in it, and everything is a dictionary there).
The author has almost no real programming experience with Python. Perl experience seems to overlap with sysadmin-related work at least partially, where it's usually used as better Bash. All their repositories in GitHub are Rust. So almost all of "real" programming they did used Rust.
Why would anyone who know Rust way better than any other language prefer to do their prototypes using anything else?
For the start, one couldn't even write a correct "if" condition in Perl without knowing if it's about numbers or strings. Even if the comparison is over the "scalars."
Second, the numbers not only exist, one can control which numbers are used when (one can explicitly force integer underlying types for some expressions or code blocks, or keep the floating point calculation, or even turn on infinite integers).
Third, Perl has scalars, arrays, hashes, references, which have even different symbols throughout:
https://en.wikipedia.org/wiki/Sigil_(computer_programming)
so the types are more obvious when reading the language lines, much more than any language where every use of the variable looks almost the same.
That's why it looks "too much like line noise" to those who don't know the meaning of the symbols. But having these symbols makes the code somehow "firmer" -- the programmer must much more often write what is expected from some variable, and the expected behavior is more obvious when reading.
It's strict, yes, but most of that strictness helps with eliminating the types of errors that make your app break over and over again at runtime. Unit tests can help bridge the gap, but who wants to write unit tests for throwaway scripts?
When you get to know Rust, it does seem to have plenty of little bits of polish to help you get things going fast. Most of the stuff that the compiler/borrow checker complains about is either easy to fix even if by throwing around clones that might not be the best performing, or represents real issues that are better to think about now than trip over halfway through some processing run.
Really? Most throwaway-script errors I get are simple things like IO mistakes, mistyped URL, or logic errors. The rust borrow checker is not designed to catch simple mistakes, it's designed for massive multi-threaded applications, e.g. making sure threads access data safely, making sure a variable doesn't get unexpectedly modified by some other file, making sure memory is owned by exactly one place, etc. But in a single-threaded garbage collected script that can fit on your monitor, why should you care? The borrow checker does not check for logic errors; it checks for the things you don't care about.
I ported some random Python to Rust six months ago: https://news.ycombinator.com/item?id=22712441 Here's the version I'd write today:
use anyhow::Result;
use chrono::NaiveDateTime;
use serde::Deserialize;
use serde_json;
#[derive(Deserialize)]
struct Person {
name: String,
age: u32,
hired: NaiveDateTime,
emails: Vec<String>,
}
fn main() -> Result<()> {
let data = std::fs::read_to_string("agenda.json")?;
let mut people: Vec<Person> = serde_json::from_str(&data)?;
people.sort_by(|a, b| b.name.cmp(&a.name));
for person in &people {
println!("{} ({}) - {}", person.name, person.age, person.hired);
for email in &person.emails {
println!(" - {}", email);
}
}
Ok(())
}
I learned that Chrono supports serde so I can remove my own parsing, and using ? instead of unwrap, because it's not harder and it ends up being nicer overall.(EDIT: Ah actually, it won't recognize this specific format automatically, so I still would write the parsing code. Even then, it's still roughly in the same ballpark.)
For me that's huge. I work in a support role where I pretty much exclusively look after code other people have written. Without type hints when something blows up I need to follow the code all the way back to whenever the variables were initially substantiated so I can know what to expect and what I can do with it. If I have a type I know what I'm looking immediately and start building a model of things without back tracking.
> That’s one of the points of Rust. It’ll complain that our code is not production quality and that we need to do better to save on the pain down the line.
Ok, Rust will catch a lot of bugs, but assuming that a program is "production quality" once the Rust compiler doesn't complain about it anymore would be really bad - it may still be full of business logic errors...
That's kinda cute.
But you dont always need them, so sometimes leaving them off works, but sometimes nothing happens because it's waiting for the semi-colon.
I'm fairly experienced and I still can't throw up an HTTP server in Rust, even with libraries.
I almost can do in with NodeJS and Express from memory.
Something like
app = require('express')
app.listen('/', function ( req, res ){ res.send (' Hi Hn') }) ; It's ok for different languages to do different things.
I can't imagine anyone being faster with Rust than Python or JS. Of course eventually Rust gets a performance boost, but it's never going to matter unless you're talking about a larger scale project.
See: https://rocket.rs/ https://actix.rs/
But, of course, the demo code is always easy. The real nightmare starts as soon as your application became complex enough, that the demo no longer fit and you need to manually spawn something (To handle TCP connection before it hits the framework for example) rather than just "actix_web::main" or "tokio::main".
I would like Flutter to take over since it combines the best of Java /C# ( types) with the best of JavaScript/ Python ( dynamic variables which can be any type ).
https://github.com/GWBasic/open_links
I spent a few hours reading the book, but it didn't sink in. I will be honest, though: Rust is still very challenging for me. The learning curve is extremely steep. Perhaps it's because Rust makes some things easy, at the expense of what automatic memory management and runtime type metadata make easy?
Slowly I'm getting faster in Rust (it does require a particular mental model). What really was the gold in this article for me was the useful crates listed. Thank you a lot for that!
This is something your hear in a lot of places. Often (not always) it seems to be spoken with regret. I think we should be quicker to celebrate than a problem has been solved (and seems to be staying solved!) without worrying so much about whether it was done "properly".
Indeed, I think the frequent success of temporary solutions should be seen as a challenge to the prevailing orthodoxy that software developers should be striving towards more discipline and process. Instead, we should be looking seriously at why quick fixes and cowboy coding work so well, and asking whether we can apply these more widely.
1. Put a quick and dirty script in to quickly solve some immediate problem
2. When that quick and dirty script stops working, instead of putting a proper solution in place you either add another quick and dirty script in to plug the issue or you modify the original quick and dirty script in some ad-hoc way
3. Repeat over and over again
And then you end up with some impossibly complex Rube Goldberg machine.
It can go either way, and I'm certain not trying to argue that quick fixes should never be replaced -- just that trade-offs should be considered.