The "Variance of slice types" paragraph is confusing as hell. The type Tree implements interface Barker, after all. To me it looks like this is more about what I call "accidental implementation" (which is one of the many reasons why I don't like Go).
Can somebody please explain what I'm missing?
Yes, it conflates a structural vs nominal type difference (i.e. punning 'bark'), with a classic variance issue.
Ignore the tree. Suppose you have _seals_, who are plausibly 'barkers' in the same sense as dogs. Take an array Dog[] and pass it to a function that accepts Barker[]. It seems reasonable to take a Barker from the array, nice and safe. But suppose we try to put a Seal into the array, that's not fine: it's a Dog[].
Famously java got this wrong, but runtime checks (somewhat) stop that being a disaster.
It's just a bad example because the dog barking and tree barking just happened to be called the same.
But let's say you have Admin and EndUser that both implement the User class. You would think you could use a []EndUser on a function that takes a []User. Because you can use a EndUser when you need a User.
Except you can't because of mutability. In Scala for example, ListBuffer is invarient (because you could append the wrong type) however the immutable List is covarient so you can call a function that takes a List[User] and pass it a List[EndUser].
The irony is that in a normal Rails app workloads are already split by default (requests and background jobs are handled by different processes that share the same codebase).
Yep, it's a very normal thing in the Rails world to separate things like this. For Django too.
I'd encourage people to look a past just the splitting of workloads though. The other point about resource limits for shared resources across workload tiers is really key, such as having very granular database connection pools even inside of a single workload deployment.
That's less common – though not unheard of – in Rails-land.
I've been writing Ruby for 20+ years and "unless" (the whole language, actually) immediately clicked for me when I first learned it.
That said, I only use it in 2 cases:
* as a trailing condition (do_something unless this), mainly for early returns
* as the only arm of a multi-line block (unless this ... end, no "else" blocks - Rubocop would yell at me anyway)
And then only if the condition is either a simple value, or a combination of simple values (this && that, this || that).
That's it. Never had a problem with double negatives or accidentally inverting the logic.
This is basically how I use unless too. Complex conditions are complicated enough to grasp without inflicting an unless on me. But for simple cases it is a nice to have feature.
Of course one can avoid it at all but of course one cannot avoid to spend time on it when reading somebody's else code.
FWIW HN does not support markdown lists (or markdown anything other than emphasis and code blocks really), you need to add an empty line between your “list items” so the comment parser treats them as separate paragraphs instead of munging them together as just one.
> - There are no booleans in the language! Conditionals can still succeed or fail, but failure is defined as returning zero values and success is defined as returning one or more values.
> How about scheduling a daily summary email? Daily reports?
Forgive me if I'm wrong (I don't know Phoenix that well), but don't you need some external library like Exq do perform background jobs? How is Phoenix+Exq different from Rails+Sidekiq?
You don't need to run a jobs server, the language itself handles the processes. Most people use a library but the work still happens in the language runtime. And you can even build your own on top of supervisors, genserver, etc.
While this implements FizzBuzz it does not actually end up doing the work at compile time.
You annotate get_fizzbuzz_equivalent() with const, so Rust would evaluate that on constant inputs at compile time, but that's not very interesting since it's basically a switch.
The use of const here does not oblige Rust to somehow figure out everywhere you can use this function and do the work at compile time since the inputs might be (and are here) variables. Sure enough if you try in Godbolt you will see that eliding const makes no real difference.
Rust's const today is far less powerful than something like C++ constexpr, I suspect that you can't really do what Nim did in a reasonable way with Rust. You could I'm sure get there with build.rs and/or proc macros, but that's not really in the spirit of this exercise.
To elaborate, the parent doesn't call get_fizzbuzz_equivalent in a "const context", which would require it to be evaluated at compile time. So it's called at runtime like it didn't have `const`.
You can do something like the nim without build.rs or proc macros:
#[derive(Debug, Copy, Clone)]
enum Value {
Fizz,
Buzz,
FizzBuzz,
Number(usize),
}
const fn get_fizzbuzz_equivalent<const N: usize>() -> [Value; N] {
let mut result = [Value::FizzBuzz; N];
let mut i: usize = 0;
while i < N {
let n = i + 1;
if n % 15 == 0 {
result[i] = Value::FizzBuzz;
} else if n % 3 == 0 {
result[i] = Value::Fizz;
} else if n % 5 == 0 {
result[i] = Value::Buzz;
} else {
result[i] = Value::Number(n);
};
i += 1;
}
result
}
fn main() {
const FIZZBUZZ: [Value; 31] = get_fizzbuzz_equivalent();
println!("{:?}", FIZZBUZZ);
}
There are certainly some ergonomic issues here; having to use while because for isn't usable in const contexts yet, which is annoying. But this does compute the whole array at compile time.
> having to use while because for isn't usable in const contexts yet
For people wondering why, both as Rust outsiders or Rust beginners:
Rust in some sense really only has one loop, just named "loop", which is an infinite loop like while(true) { } in various other languages. Other looping is just syntactic sugar for "loop" specifying some way to escape the loop. Rust's for loop is sugar for a "loop" that uses the IntoIterator trait to get an Iterator and then call next() on it each time the loop repeats, until it breaks out of the loop when next() returns None.
Unfortunately, Iterator::next() isn't a const function. Your actual implementation of next() for trivial data structures probably is constant, but it wants specifically Iterator::next() and that can't be labelled constant today because it's just an implementation of a trait and some other implementations are presumably not constant.
As a result, even though all Rust's loops are just "loop" and "loop" is allowed in a constant function, you can't use the for loop because Iterator::next() is never constant so now your function isn't either.
A proper fix for this would be pretty cool, but is not easy to do.
Yep, you're perfectly right of course. My "solution" was mainly to address the problem "how can I return a String from a const function"... the answer is that you don't have to :-)
> There are no tags, or labels, no other folders or any other buckets of organization.
Actually there are labels (and what's the difference between a tag and a label?)
> [...] there’s no automatic organization here
When you get an email from a new contact you decide where it (and future emails) should go; you can also change the destination after it has landed in your Imbox.
> It is Slow & Ugly
This is very subjective: I like the UI a lot, and I've never perceived any slowness (Gmail, on the other hand...)
> The thing that DHH is failing to mention is that Monoliths work great when your application looks something like Basecamp.
The thing that people fail to grasp is that (almost) everything DHH writes is in the context of "when your application looks something like Basecamp" :-)
I've been following 37signals/Basecamp since 2002, I think (long before Rails) and they've always made it clear that their opinions/advice are _not_ universal, because they only speak from experience - _their_ experience.
So, if you're not in a similar situation as theirs, then of course their arguments are going to look strawman-ish to you.
And while he does that to excuse himself from having to reiterate that later, the fact that he doesn't anyways leaves a reader by the end thinking "Damn, maybe he's right. Maybe all software should be this way." even when it shouldn't.