118 karma · joined September 1, 2007
I think you mean hardlinks the object database? The index (staging area) changes constantly. Not much point in hardlinking it.
Hardlinking the object database is the default behavior with you clone locally on linux. It's great for one-off clones such as CI/CD pipelines and agentic containers, but the benefit in terms of disk space saved is short-lived: as repos evolve independently, they replace their packfiles with new ones, and the new ones will not be hardlinked because they don't contain the same objects.
I like to keep bare repositories in dropbox, but I use `--no-hardlinks` when cloning, because before I did that, on one occasion, dropbox corrupted the bare repo, and my working repo was corrupted as a result.
pub trait ToOwned {
type Owned: Borrow<Self>;
fn clone_into(&self, target: &mut Self::Owned) { ... }
}
Here the `Owned` is technically an input to `clone_into`, but of course semantically it's still an output.A more subtle one is `StreamExt`:
pub trait StreamExt: Stream {
type Item;
fn map<T, F>(self, f: F) -> Map<Self, F>
where F: FnMut(Self::Item) -> T,
Self: Sized { ... }
}
Here, the associated type `Item` is the input to the mapping function, and since the mapping function is an input to the map function, it is an input-to-an-input - which basically makes it an output. i.e. the stream "outputs" the items into the mapping function. (aka two contravariants make a covariant).I couldn't find any more direct examples.
pub trait Mul<Rhs> {
type Output;
fn mul(self, rhs: Rhs) -> Self::Output;
}
This begins the open declaration of the compile-time `Mul` function. It has two inputs: `Rhs` and `Self`. It has two outputs: `Output` and `mul` (the top-level runtime function).Note that we haven't defined the compile-time `Mul` function yet. We've only opened its definition. It's sort of like writing down the type of a function before we write down its implementation.
The implementation is never written down, though, because it is always the same: a lookup in a table that is constructed at compile-time. Every impl fills in one cell of the compile-time table.
impl Mul<f32> for i32 {
type Output = f32;
fn mul(self, rhs: f32) -> Self::Output {
self as f32 * rhs
}
}
This adds a single cell to the `Mul` table. In psuedo-code, it's like we are saying: Mul[(i32,f32)] = (Output=f32, mul={self as f32 * rhs})
The cell is a pair of a type and a function. For traits with lots of functions, the cell is going to be mostly functions.The main thing I'm pointing out (that the author didn't already say) is that `mul={self as f32 * rhs}` is also part of the compile-time table, not just `Output=f32`. The author says that associated types are no more than the return of a type-level function, and I want to clarify that this isn't a metaphor or mental short-hand. Traits ALWAYS HAVE BEEN type-level functions. They input types and output mostly functions. Associated types just allow them to output types in addition to outputting functions. Notice how associated types are defined inside the curly braces of an `impl`, just like the functions are.
Once you realize this, it's all very simple. I think there are a few things that obscure this simplicity from beginners:
1. `Self` is an implicit input to the compile-time function, with its own special syntax, and for many traits it is the ONLY input. When reading a book on rust, the first examples you encounter won't have (other) type parameters, and so it's easy to overlook the fact that traits are compile-time functions.
2. Rust traits are syntactically similar to object-oriented polymorphism, but semantically duals of each other, so experienced OO programmers can jump to wrong conclusions about rust traits. Rust traits are compile-time and universally typed. Object-oriented polymorphism is run-time and existentially typed.
3. Because the trait-as-compile-time-function's implementation is so highly structured (it's just a table), it can actually be run backwards as well as forwards. Like a prolog predicate, there are 2^(#inputs+#outputs) ways to "invoke" it, and the type-inference engine behaves more like a logical language than a functional language, so from a certain perspective associated types can sometimes look like inputs and type parameters can sometimes look like outputs. The reason we call them functions and not merely relations is because they conform to the rule "unique inputs determine unique outputs".
> This isn’t true (yet)
It's at least partially true:
Amazon = 238.08 Netflix = 219.32 Google = 73.95 Facebook = 68.36 Apple = 63.17
The Shiller PE Ratio of the S&P 500 is current ~30. All of these companies are overpriced, and Amazon and Netflix significantly so.
In the google search results, click the three vertical dots above the link and to the right of the domain. If using mobile, you'll need to switch to desktop mode to see the three dots. After clicking, an "About this result" pane will pop up to the right, probably[1]. In that pane you'll see the true link, and you can Right Click > Copy Link.
[1]: On my computer, the "About this result" pane says "BETA", so not sure if everyone can use it. It works for me in a private window, though.
1: https://www.lesswrong.com/posts/AnHJX42C6r6deohTG/bell-s-the...
2: https://kantin.sabanciuniv.edu/sites/kantin.sabanciuniv.edu/...
Because of the expansion of the universe, it is theoretically possible for me to get in a space ship and travel away from you at such a high speed that we would never be able to meet again, even if at some point we both decided to head back toward each other as fast as possible, and even if we were both immortal. Our light cones would no longer intersect.
Probably you would assume that I continue to exist, despite that for all intents and purposes I cannot affect your life in anyway and therefore "don't" exist.
Similarly if I crossed the event horizon of a black hole.
I believe that a qubit is in superposition (i.e. exists in multiple miniature "universes"), because that theory produces results that are consistent with experiment. When the qubit's universes de-cohere with mine, such that for all intents and purposes I can now only ever observe the qubit in one particular state, why should I assume that the other universes have ceased to exist? Decoherence is a continuous process, just like traveling outside of a light cone, so why treat them any differently. It's rather arbitrary to say that increased distance in space preserves existence, while increased distance in phase space does not.
If the latter, then I would ask, "Do you believe the ocean is deep, even though you can only see the surface?"
My summary:
The paper compares two different ways of dividing a codebase into modules. The program under question is KWIC. In the first case, the modules are divided into the four major steps of executing the KWIC algorithm: Input, Circular Shift, Alphabetizing, and Output. In the second case, the modules are nearly the same, but they are more service-oriented than action-oriented, e.g. Circular Shifter and Alphabetizer instead of Circular Shift and Alphabetizing. The chief difference is the interfaces between the two. In the first case, the contract between modules is the format of the data structures being passed from step to step. In the second case, the data structures are encapsulated, and the contract between modules is the api for manipulating the hidden data structures.
The author argues that the second way is better in terms of changeability, independence of development, comprehensibility.
At this point I lost interest in the paper (because I sing in the choir), but scanning the rest, the author talks about how to avoid taking a performance penalty when using the second design, and then gives a second example, this time of a markov algorithm translator instead of a KWIC.
case class HttpCall[OutputType](
verb: String,
url: String,
queryParams: List[(String,String)],
formParams: List[(String,String)],
resultType: Class[OutputType])
trait Api {
def getMyTasks(): HttpCall[List[Task]]
def getTask(taskId: String): HttpCall[Task]
}
The SDK can also provide a driver for making the http calls: trait Driver {
def call[Out](httpCall: HttpCall[Out]): Out
}
So now api invocations look like this: val myTasks: List[Task] = driver.call(api.getMyTasks())
val aTask: Task = driver.call(api.getTask("foo"))
What's nice about this design is that the consumer can write their own driver if they don't like yours. Prefer asynchronous over synchronous? No problem! trait Driver {
def call[Out](httpCall: HttpCall[Out]): Future[Out]
}
Everything in the article except for grep-ability can be achieved with this kind of design.A*, Chebyshev, Allow Diagonal:
WWWWWW
GWR
WWWWW
Optimal path should go underneath, but the simulator chooses to go over.