You keep saying I'm offended like it's some sort of magical word to dismiss what I'm telling you. You keep making assumptions and accusations about me and an entire fandom, for what reasons? Because we like a game you don't?
Grow up.
2,632 karma · joined June 19, 2019
- https://link-society.com
- https://krouter.cloud
- https://flowg.cloud
- https://linkdd.github.io
- https://david-delassus.medium.comYou keep saying I'm offended like it's some sort of magical word to dismiss what I'm telling you. You keep making assumptions and accusations about me and an entire fandom, for what reasons? Because we like a game you don't?
Grow up.
So this is not hate to you? Basically insulting a complete generation, fans of a franchise, making dubious comparison with unrelated things and accusations of pedophilia?
Are you a joke?
> Bring arguments please
Read siblings comments, I already explained my point of view.
Your comment is full of hate because someone basically have a different opinion than you. This is toxic and I expect it to get flagged pretty quickly.
Games are for every one (well some games are for mature audience but that's not the point).
I like easy games because I don't want to be punished for not being good enough after a job week.
I like thinking/strategy games like chess because I like to challenge myself.
I like a lot of other genre for different reasons.
But not a single one of them should be depicted as "for little kids", not even sokoban, tictactoe, etc... because that's just not true.
Games are for entertainment. You're either entertained, or not when playing the game. It's completely subjective. It does not depend on the fact you're a kid or an adult.
No need to insult the fanbase. You can just say "I don't like it".
> it just goes to show that people like different things.
Which is why I simply explained what worked for me and what didn't work for me, not saying any of my comment was an absolute truth.
While quite different of course, they have basically the same goal: you first have to get lost in order to find your way.
The fun begins when you explore and stumble across things by mistake.
Also many landmarks in BOTW are straight up taken from the NES game, like the twin mountains in the gerudo desert.
After Ocarina of Time, the Zelda games went to a linear story with a dungeon formula almost copy pasted for 20 years. BOTW breaks this pattern to get back to its roots: exploration.
Just watch the interviews of Aonuma explaining this.
Nintendo's open-world is the only open-world I liked.
I can't get immersed in GTA's open world because I'm just a random going on a killing spree for 15 minutes then shut the game down.
I hated Skyrim, the world is big but empty. There is nothing particular about the landscape that makes me want to "go there". Most of the time, I discovered areas because I was going in straight lines.
Zelda on the other hand, I was like "oh wow, a mountain split in two, I wanna see it". The landscape view makes you want to see what's going on and act as a map ("oh so the gemini mountain is on my right, I must be at XXX"). You're on top of a tower, look at the volcano, and you're like "what is that thing moving on the horizon". Same thing for pretty much all divine beasts.
The other nice thing about BOTW's open world is the map. You don't need it. In The Witcher 3 you have a GPS that tells you where to go. When people tells you to go somewhere in this Zelda, you ask people, you look at the road signs, and you go there. This makes the world much more realistic and less empty because you need to interact with it.
To go to the Goron village, you have many ways of doing it, stockpile a huge amount of food to heal while you burn, or go around and talk to people to find the potion. If you choose to stockpile food, you need to hunt, to cook, and to interact with the world once again.
The world is part of the game play, unlike many other open world games where the world is just the scenery where the game play take place.
On my way to the 1.0.0 by implementing the automated test suite!
> Would be cool to eventually have a recipe where you import the crate, then program the deploy in rust
This is actually one of the goals and already somewhat possible since v0.5.0 (i'm at v0.8.0 now, working on the test suite to reach 1.0.0) if you don't mind writing shell commands as strings in Rust :P
The currently available tasks are:
- info: to get what's in the inventory (and later infos about the remote host)
- exec: to run a (templated) shell command
- upload: to upload files or render templates
- download: to download files
With this, you can already upload archives, extract them, create systemd files, start the service, etc...It's a bit more manual than with ansible/puppet/... but it works :)
I think, at the moment, a more correct comparison would be with Fabric[0] in Python. I have some ideas for post-1.0.0 relying on TCP/IP tunneling through the SSH session (docker builds on remotes, querying remote services hiding behind an SSH bastion, ...), I'd love to hear more ideas though :)
[0] - https://www.fabfile.org/https://baptiste-wicht.com/posts/2012/11/cpp-benchmark-vecto...
> some of us have been around the block a few times though, and now need to make sure those spaces are delineated for others in a way that won't force them into a performance corner.
This, just like the rest of your comment, is just patronizing and condescendant.
> I don't mean to brag, but I guess I'm a lot better at planning ahead than you
See previous point...
> I also can't remember any time I had to reach for a hammer as big as reflect and didn't expect to very early on
This is not what I said at all. Let's say you know early on, before any code is written, you will need reflection. Can you tell me how many calls to the reflection API will happen before-hand? Is it `n`? `nlog(n)`? `n²`? Will you use reflection at every corner, or just on the boundaries of your task? Once implemented, could it be refactored in a simpler way? You don't know until you wrote the code.
> most of the time I know what I intend to do to my data
"what" is the spec, "how" is the code, and there is multiple answers to the "how", until you write them and benchmark them, you can't know for sure which one is the best, you can only have assumptions/hypothesis. Unless you're doing constantly exactly the same thing.
> but also maybe think about how many pointers you're going to have to chase and methods your users will need to implement in the first place because you probably don't get to "optimize" those later without breaking the API.
Basically, "write the spec before jumping into code". Which is the basis of "make it work, make it right, make it fast" because if you don't even know what problem you're solving, there is no way you can do anything relevant.
> You write about "the bottleneck", but there's not always a single bottleneck distinct from "the API".
I never implied there is a single bottleneck. But If you separate the implementation details from the High-Level API, they sure are distinct. For example, you can solve the N+1 problem in a GraphQL API without changing its schema.
If your implementation details leaks to your API, it just means it's poorly separated.
> You're never going to make an API like PyReader anywhere near as fast as GoReader, no matter how much optimization you do!
Because Python is interpreted and Go is compiled. Under the hood, the OS uses the `int read(int fd, void dest, size_t count)`, and there is an upper limit to the `count` parameter (specific to the OS/kernel).
Python's IO API knows this and allocates a buffer only once under the hood, it would be equivalent to having a PyReader implementation using a GoReader interface + preallocated []byte slice.
I can't tell you which one is faster without a benchmark because the difference is so subtle, so I won't.
And I never said anything remotely close to contradict this statement.
> Knowing where performance issues with certain techniques might arise is not premature optimization.
It is:
- Python: should I use a for loop, a list comprehension or the map function?
- C++: should I use a std::list, std::vector, ...?
- Go: should I use interface{} or generics?
The difference between those options is subtle and completely unrelated to the problem you want to solve.> Implement with an appropriate level of care, including performance concerns.
Step 1: solve your problem naively, aka: make it work
Step 2: add tests, separate business logic from implementation details, aka: make it right
Step 3: profile / benchmark to see where the chokepoints are and optimize them, aka: make it fast
Chances are that if you have deeply nested loops, generics vs interface{} will be the last of your problems.To take the C++ example again, until you have implemented your algorithm, you don't know what kind of operations (and how often) you will do with your container. So you can't know whether std::list or std::vector fits best.
In Go, until you have implemented your algorithm, you don't know how often you will have to use generics / reflection, so you can't know what will be the true impact on your code.
The "I know X is almost always faster so i'll use it instead of Y" will bite you more often than you can count.
> Not every kind of poor performance appears as a clear spike in a call graph
CPU usage, memory consumption, idling/waiting times, etc... Those are the kind of metrics you care about when benchmarking your code. No one said you only look at spike in a call graph.
But still, to look for such information, you need to have at least a first implementation of your problem's solution. Doing this before is a waste of time and energy because 80% of the time, your assumptions are wrong.
> and even fewer can be fixed without changing any external API.
This is why you "make it work" and "make it right" before you "make it fast".
This way you have a clear separation between your API and your implementation details.
Just implement naively, then if you have performance issues identify the bottleneck.
From this, we can define a new relation called "=" such as:
for all x in A and for all y in B, x in B and y in A
We don't care what A, B, x or y are. We don't care what "x in A" means. It's just logical statements that are useful, and we build the rest of maths upon it.For me "=" does not mean "equal", "equivalent", or anything. It means "there is a relation between 2 objects".
For example:
- in a programming language "a = 3" means that "a" is related to "3"
- in a BNF grammar "rule := term term term" means that "rule" is related to "term term term"
- in a math equation "y = x + 1" means there is a relation between "y" and "x + 1"
- on some old calculator "A <- 1" means there is a relation between "A" and "1"
In each of those domains, the relation is precisely defined. But it does not need to be the same relation (they are different math theories).Another example, "y = x + 1" could be interpreted as "there is a value for x for which I can replace x + 1 by y". You don't even have to care about what "x + 1" means.
Sequentially, one after the other, then it aggregates the `Result<(exit_code, output), Error>` type into a JSON.
Once I take a look at async programming in Rust, it should be trivial to add the concurrent option: just start them all, wait for them, and aggregate as before.
Still not sure if it should be the default or not, should it be a `--sequential` or a `--parallel` flag? IIRC, Ansible does things sequentially too.
I'll add this feature to the roadmap then :)
> I don't know that a full parser for Boolean logic is necessary - even having a complimentary `-T` option that takes mandatory tag names might be enough.
I truly dislike command lines with hundreds of arguments. IMHO, A boolean logic would be clearer on the intent than a combination of -t -T -not-T etc...
> so it's multiple background SSH processes.
I'm not sure yet if parallelization should be the default or not. Also I've not yet learned Tokio and Async programming in Rust :P
> some way to have per-host data sent
Yes, just like Ansible evaluates very variable per host. At the moment you can do it manually:
res=$(tricorder -i inventory -- echo "available")
host_ids=$(echo $res | jq "some jq black magic")
for host_id in $host_ids
do
tricorder -i inventory -H $host_id -- echo "specific data sent to $host_id"
done
Maybe i'll make use of the `tinytemplate` crate and add a -D option for data that would be similar to the inventory (TOML static file or program/script outputting JSON).I'm not sure yet on how it should look.
I mean to keep tricorder KISS so it can be integrated easily in a bash script as a standalone command, and not a full-on folder structure like ansible playbooks.
> I gather it just allows one to run the same command over multiple hosts (filtered by name or tag), but nothing beyond that?
For now. I intend to carefully extend it to other features, like in said in the original post:
- mimic the "gather facts" feature from ansible so you can query it with `jq` - provide an API in a few programming languages so that you can choose whatever fits your needs to "script" the logic (and with Rust/Go, distributing a single binary as a 'setup.exe' should be plenty enough) - probably a plugin system similar to kubectl (as in subcommands look in PATH for a binary named `tricorder-subcommand`)
I'd like to see it becoming a real replacement for YAML based workflows, but I started small because I don't want to repeat the same mistakes. I'd like to see how people would use this before building on it.
I'm not set on this, maybe I will add a simple parser for something like `(A|B)&(~C)`. What do you think ?
You may need to support older versions because somewhere, you will have a user who don't want to upgrade.
This means you have to maintain multiple versions of your documentation.
Quickly sent a mail to LWN and got their authorization in the next 10 minutes.
Big up to the team and awesome content! This motivated me to buy a subscription to support them.
[0] - https://linuxfr.org/users/linkdd/journaux/lwn-une-porte-de-sortie-pour-a-out-63380a9d-20e0-44de-979d-74afcb6f3910This sums up your incomprehension. `time.Seconds` is just a constant. An integer with the value `1_000_000` meaning 1 million of nanoseconds.
In an expression of the form `a * b` you should always read `a` and `b` as constants. This is true for EVERY programming language.
> it isn’t inconceivable that unitlessValue * valuelessUnit * valuelessUnit = unitlessValue * valuelessUnit.
It is. For example, what would be the meaning of this:
struct Foo {
// ...
}
2 * Foo
Valueless unit (or any type) is just not a thing, not in math, not in any programming language.> But you seem to be imposing “mathematical expression” on an expression space where that’s already not the case? Whatever you may think of operator overloading, it is a thing that exists and it is a thing that “makes sense” to people using it idiomatically.
Operator overloading works on typed values, not "valueless" types. In some programming languages (like Python), class are values too, but why implement `a * MyClass` when you can write `MyClass(a)` which is 100% clearer on the intent?
Using operator overloading for types to implement casting is just black magic.
> expressions which look like maths don’t necessarily have a corresponding mathematical representation
Programming languages and the whole field of Computer Science is a branch of mathematics. They are not a natural language like english or german. They are an extension of maths.
> An equals infix operator in maths is a statement, establishing an immutable fact.
An operator only has meaning within the theory you use it.
For example:
`Matrix_A * Matrix_B` is not the same `*` as `Number_A * Number_B`
`1 + 2` is not the same `+` as `1 + 2 + 3 + ...`
`a = 3` in a math theorem is ont the same `=` as `a = 3` in a programming language (and that depends on the programming language)
As long as the theory defines the operators and the rules on how to use them, it does not matter which symbol you use. I can write a language where you have `<-` instead of `=`, and the mathematical rules (precedence, associativity, commutativity, ...) will be the same.> Reacting as if it’s impossibly absurd that someone might read 1 * time.Seconds * time.Seconds as anything other than 1 * 1s * 1s is just snobbery.
First, that's not what I said. You should read that as `scalar * constant * constant` because reading that as `scalar * unit * unit` does not make sense nor in math, nor in any programming language.
If caring about readability and consistency is snobbery, then so be it.
> Not knowing Go, I focused on the syntax and the explicit values, and tried to build a syntax tree on top of it.
And the syntax is pretty explicit, because it's the same as math or any programming language: `scalar * constant * constant`. This is why using math as a point of reference is useful, you can easily make sense of what you're reading, no matter the syntax.
> I am a fan of lisps, so my mental syntax model was (* (* 1 time.Seconds) time.Seconds))
I still read this as `(* (* scalar constant) constant))`. And I expect your compiler/interpreter to throw an error if `time.Seconds` is anything without a clear value to evaluate the expression properly.
And I would expect to read `(* (* 1 (seconds 1) (seconds 1)))` as `scalar * quantity * quantity`, and I would expect to get square seconds as an output.
Anything else would not be correct and have little to no use.
And the syntax is `3<unit>` not `3 * unit`
- 1 is a scalar - 1m is a quantity - 2 * 1m "casts" 2 to a meter, but really this is just multiplying a quantity by a scalar - 2 * 1m * 1m "casts" 2 to meter², multiplying 2 quantities then by a scalar
I insist, `1 * m` does not make sense. This is not a valid mathematical expression, because a unit can never be on its own without a value.
> expression of a unit without a value is (or should be to my mind, based on my education) a cast
There is no casting in math. Mainly because there is no types, only objects with operations. A vector is not a scalar and you can't cast it into a scalar.
A quantity is not a scalar either, and you can't cast one into another.
A quantity is an object, you can multiply 2 quantities together, but you can't add them if they are different. You can multiply a quantity to a scalar, but you still can't add a scalar to a quantity.
You cannot have a unit on its own without a scalar value. It makes no sense.
EDIT: Yes, you multiply the units `2m * 2s` : you first multiply the units to get: `m.s`. This is what I say: you convert everything to the same units before doing the calculations.
> Multiplying by the same unit is semantically equivalent to “x times 1 is identity(x)”
This is wrong.
1kg * 1kg = 1kg² period.
What you're saying is `2kg * 1 = 2kg`, which is right, because `1` is a scalar while `2kg` is a quantity. This is completely different than multiplying 2 quantities.
> It would not imply I’m multiplying the units, but rather the scalar value of the unit.
That's where you're wrong. When doing arithmetic on quantities, you have 2 equations:
x = 2kg * 4s
unit(x) = kg * s = kg.s
scalar(x) = 2 * 4 = 8
x = 8 kg.s
Or x = 5m / 2s
x = (5/2) m/s
x = 2.5 m/s
There is a meaning to units and the operation you do with them. `5m / 2s` is 5 meters in 2 seconds, which is the speed `2.5 m/s`.`2m + 1s` has no meaning, therefore you can't do anything with the scalar values, and the result remains `2m + 1s`, not `3 (m+s)`.
But assuming your comment is not a joke. You probably want to convert minutes to seconds in order to work with the same units, then add the scalar parts together.
That's how you deal with different quantities: convert to same unit, add values.
This is analog to fractions: 1/2 + 1/4 = 2/4 + 1/4 = (2+1)/4 = 3/4.
[1] - https://en.wikipedia.org/wiki/Leap_secondWhich is wrong, 1s * 1s = 1s².
For example, the force of gravity is expressed in m/s² and describe an acceleration (m/s / s, aka a change of velocity per time units, where velocity is a change of distance per time units).