622 karma · joined December 1, 2011
http://ben.kirw.in https://github.com/bkirwi ben@kirw.in
This might still be fine, depending on your needs, but IMO a legitimate distinction.
Not super familiar with Pijul though!
It's also an odd definition of professional victim, where publicly being a 'victim' results in you getting booted out of the profession.
I don't want to comment on the event itself here -- but I think that the aftermath, and the continued reaction to it, say a lot of sad things about the tech community.
- Any monoid operation is trivially parallelizable: take the dataset, split it into chunks, combine all the elements in each chunk together, then combine those results together as a final step.
- If I'm updating a row in my database with a monoid operation, I can always 'pre-aggregate' a batch of values together on the client side before taking that result and combining it with the stored value.
- If I store some statistics for every day of the year, I can calculate the monthly statistics very cheaply -- as long as those stats are a monoid.
The monoid abstraction seems weird at first, because it's so dang general, but it ends up hitting a bit of a sweet spot: the rules are just strong enough to be useful for a bunch of things, but simple enough that be applied to all kinds of different situations. You can think of it kind of like a hub-and-spokes thing -- this interface connects all kinds of different data types to all kinds of different cool situations, so you get a lot of functionality with a lot less typing and thinking.
A monoid is made up of:
- Some arbitrary type of thing. (Lists, sets, strings, numbers, whatever.) Let's call it 'A'.
- A value of type A. Let's call it the 'empty' value.
- A function that takes two A values as arguments, and returns a single one. Let's call it 'combine' function.
Here are some examples of monoids:
- String concatenation: 'combine' is string concatenation, and 'empty' is the empty string.
- Integer plus: 'combine' is the usual + operation, and 'empty' is the value 0.
- Set union: 'combine' is the union of two sets, and 'empty' is the empty set.
You can come up with monoids for all sorts of things: maps, lists, functions, bloom filters, futures / promises, ...
There are, however, a couple rules:
- It doesn't matter which order you combine things in: `("hello" + "world") + "!"` has the same result as `"hello " + ("world" + "!")`; and `1 + (2 + 3)` has the same result as `(1 + 2) + 3`.
- You can add the 'empty' value to either end without changing anything. `"" + "hello"` is the same as `"hello" + ""` and plain old `"hello"`. Likewise, `3 + 0 == 0 + 3 == 3`.
That's it! To get an intuition, you might want to pick a couple more types from the list above and pick a good 'empty' / 'combine' function that satisfies the rules.
Of course, why you'd want to do this is another question. (And one I'd be happy to answer, if you're curious.)
-- little-endian sequence of bytes to arbitrary integral type
integerWithBytes :: (Bits a, Integral a) => [Word8] -> a
integerWithBytes = foldr (\byte acc -> (acc `shiftL` 8) + fromIntegral byte) 0
At least, I'm pretty sure that's what the article was trying to do...I don't think this is some amazing showcase of Haskell; after all, the article includes a (working?) implementation in C++. But it does serve as a nice counterexample to the idea -- expressed elsewhere in the thread -- that being generic is necessarily a hugely painful or complicated thing.
http://ferd.ca/queues-don-t-fix-overload.html
Queues and similar things help flatten out the spikes, but they don't add capacity. Like many of these tools, use with discretion.
http://stackoverflow.com/questions/2982012/haskells-typeclas...
The main similarity is that 'methods' are defined 'outside' the data type they belong to. But there are a huge number of differences: Haskell's typeclasses are used for many things that don't look like methods at all, while Go's behave more like a classic OOP interface you'd find in Java.
So, perhaps superficially similar-looking, but they work quite differently in practice.
Sure, but you don't magically make society robust by telling racist jokes. The healthy society comes first; the fact that these jokes no longer sting is just a pleasant side effect.
Re: optimistic locking... there are a couple proposals for this floating around, and I'm not sure which / if one will get in. It certainly seems consistent with the general mission, though.
[0] http://engineering.linkedin.com/distributed-systems/log-what...
I enjoyed and totally endorse the content of your blog post, but I did find the title / intro a bit misleading. Exactly-once is pretty much always what people want: it's the easiest model for most folks to reason about, especially when they're new to the whole distributed-systems thing. (If exactly-once delivery was easy, you certainly wouldn't find people going out of their way to build systems that dropped or duplicated messages.) Of course, you might not need exactly-once delivery to get what you want -- which is good, because like you say, that's a hard / impossible problem to solve in general.
It feels to me like people in the stream processing universe have taken that message a bit too much to heart -- things like the 'lambda architecture' treat stream processing as "too hard to get right" and relegate it to approximate, disposable calculations only. My post was partly an effort to push back on this; as a community, I don't think we should give up without a fight.
The Samza ecosystem seems to be a magnet for this sort of thing these days -- the community's really committed to taking full advantage of Kafka, and not just papering over it so it looks just like any other queuing system. Really excited to see where all this will lead.
Clojure's a nice example: it's been hugely successful, but I have a hard time imagining anyone raising two years' worth of salary up front to work on their idea for an idiosyncratic Lisp implementation. It looks like he had to fund that work himself; how much more innovation would you see if this kind of extended project was common?
[0]: http://research.microsoft.com/en-us/um/people/lamport/pubs/b...
var (
ch = make(chan *http.Response)
err error
)
....
return func() (*http.Response, error) {
return <-ch, err
}
It seems to me like calling that returned function is destructive -- if you block on it more than one, it would block forever. Is that right? It seems like a different contract than the usual Future / Promise shtick.The thing I find really compelling about this paper is that it's not tied to a single coordination method, though -- it pulls everything back to application level invariants. Human beings have a bad track record of reasoning about invariants in distributed systems, but computers are pretty good at that sort of thing... if we can specify our invariants explicitly, and let the machine figure out how exactly to spread that work out on the cluster, I think we'll come out far ahead.
- Trying to get rid of a specific problematic dependency? It's easy to filter a DAG to only those paths that lead to a particular node.
- Trying to reduce the raw number of dependencies? Consider collapsing clusters of transitive dependencies down to a single node. (If one of your direct dependencies pulls in 20 unique deps of its own, you don't really need to know what they are or how they're connected to each other -- just that there are 20 of them.)