4,046 karma · joined December 7, 2017
I'm also working on the Tenet language; see https://tenet-lang.org/
Also at https://keybase.io/ben509
YdRK9Q3mIBRWsRBonHhJCsgLnurwJk9eHbw1Jg4wz9M
Tips are an awful way to incentivize good service.
I'm neither qualified to judge my server's performance, and I've got very limited and potentially misleading information about what a reasonable expectation is. I've got no idea what small crises my server had to deal with, or how other customers are treating him.
And because tips are everywhere now, nobody knows the going rate for most tips. Obviously tourists have no idea how much to tip.
If I reduce the tip, the waiter is only finding out that I was unsatisfied after it's too late to fix anything. But tipping up front makes the exercise entirely pointless.
And the moralizing of tips is stupid.
Many moral arguments paint people in the service industry as paupers or victims, but a basic level of respsect has to assume they're competent professionals who chose this line of work.
A tip is not an act of generosity, either, you're paying a fee for a service rendered. And that's why, as a system, the morality of tipping is entirely backwards: it rewards people who rip off their servers.
The thing that really bothers me are the awful motivations behind tipping: being control freaks, conspicuous consumption and all the slimy business incentives to push their payroll onto their customers.
What F#'s aversion to cyclic dependencies does forces you to break cycles of types referring to types referring to types. The way you do that is by writing a generic function that avoids referring to a concrete type. Because it's generic, such a function is inherently easier to test by means of a simple stub value.
1. It tends to retard technical progress.
2. They have to make deals with entrenched incumbents to do finance.
3. There's nothing stopping a hedge fund manipulating Alphabet's stock.
You would also be censoring people if you ran an upscale restaurant with a dress code and standards of behavior, or if you didn't let them post ads on the walls.
As long as you make your rules clear and enforce them fairly, censorship and standards of behavior are completely fine.
The very large social media platforms all market themselves as being neutral. They got to be the largest by providing a space for everyone.
So the ask here is "please enforce the rules fairly the way you have always promised you would." Asking a business to deliver on the promises they've made and continue to make is not an entitled attitude.
After all, all these businesses are in complete control of what they promise. If they don't like the old promise, they have a first amendment right to make a new promise that suits them better.
You are using Python's class mechanism, but that's where the OOness ends. The types themselves are immutable value types, carrying no state and reacting to no messages, and they don't exploit a class hierarchy beyond having a common "Number" class. The "methods" don't even behave like methods, e.g. __add__(self, other) has special wiring so it obscures which side of the addition is "self".
That's very different than a classic OO scheme like a UI toolkit.
I think the reaction to it is more the "everything is mutable" approach most OO languages took, which led to things that looked organized but that were a hot mess of side-effects.
With dataclasses (or attrs) you can cut back on that by freezing everything, and still get the clarity of methods that lay out the essential functionality of a type.
In their own example, "get_items" and "save_items" make a bit more sense if they're stashed away in the Client namespace, and you can see that they're essential to what a Client does.
And while Oil's conjecture is probably right, I'm not sure it survives if you add the caveat, "in that same language."
For instance, Python doesn't have a native way of expressing sum types outside of inheritance. That's not to say inheritance, especially an always open style, is a good way of working with a sum type, it's just the only native way.
If you generate a random URL, you'll always get a cache miss.
If you use a static URL, you'll know if you have a new session or not, but that doesn't tell you what the tracking ID was.
The only thing I can imagine is the server serve several images /byte1.png /byte2.png etc. and make them all X by 1 pixels, encoding a random value in the dimensions, assuming that's available to Javascript.
But if you encode the tracking ID in the image somehow, you don't care much whether it was cached or not, it's inherently persistent. It'd mainly be useful if you're trying to reconstruct a super cookie.
It was also easier to install it on my parents' computers than to convince them to change browsers.
It always bugs me that everything distributed inevitably devolves into polling...
> The function app hosts the scheduled process that at the specified time retrieves the list of files from the storage account (1), iterates through them, for each (2) one checks if it changed and sends a notification if it did (3). If the file has changed, the function app also updates the file hash in the storage account (4).
The cheaper way to do it would be to set up a webhook listening for push events [1]. You do have to handle missed webhook notifications, but you can usually just do that by polling very infrequently.
[1]: https://docs.github.com/en/developers/webhooks-and-events/gi...
You're right, some languages are worse about this, e.g. Python's pickle will perform arbitrary code executions.
But it's easy enough to restrict it to a set of known good constructors. You still have the classic late-binding dilemma: it's flexibility we don't actually want. Why should my find_matching_socks function ever have to worry that it might be passed basketballs?
The usual answer is, "it's fine, it'll just crash if they do that."
It might crash, or it might hang, or silently corrupt something. An attacker can stick the wrong kind of object into your code anywhere they like.
And this includes intermediate processors that are invoked manually, so if someone uses a command like jq to simply test something, numbers are being truncated and they're baffled as to why they're getting different results.
You inevitably run the risk that your deserializer can, effectively, make arbitrary function calls. And your data is tied not only to your language, but also needs to find classes in specific modules.
I took a shot at it with https://pypi.org/project/json-syntax/ after doing some ad hoc solutions.
My approach was:
1. Don't serialize directly to JSON, but convert to the "jsonic" types. 2. Allow users to pick the rules they need, and make it reasonably easy to create new rules. 3. Generate picklable encoding and decoding functions.
It's worked pretty well for a project that has a very complex set of objects that need to be transmitted via JSON. The main difficulty is that it depends on interrogating Python's type annotations, which were pretty sketchy in earlier versions.
You probably could run GraalPython, which is Python 3 and Jython, which is 2.7, since they both run inside a JVM.[1]
There'd be no interop between the languages, though. To get that in Graal, you'd have to reimplement python 2 using Truffle.
That's a massive effort compared to simply running 2to3 to migrate your old code to Python 3.
[1]: https://www.graalvm.org/reference-manual/python/Jython/
Keep in mind that while a human being is very good at categorizing things, they're terrible at doing it over and over again, so the scarce talents are likely consistency and conscientiousness.
You should expect that, overall, the (n+1)th person you hire is marginally less talented than the nth. And while people are constantly entering and exiting the labor pool, this is negated by the fact that you're trying to expand your share of it.
So while there's a lot of randomness in recruiting, if you enforce any kind of standard, you'll hit a point where not enough candidates meet that standard, so you have to drop it. Once it is below the threshold of "better than AI" you have no reason to hire more people.
And you'll try to expand your eligible pool by raising wages, but everyone else wants consistent, conscientious people, too, so you're liable to run into the economic constraint: the amount you have to pay someone will exceed the value they're adding as a moderator.
It doesn't make a lot of business sense to say, "we invite everyone in" and then plan on kicking out a lot of users.
There are loads of simpler reasons why lefties thought they were getting booted, especially incompetence on Parler's part.
The EC was more than a compromise, it was an integral part of a system that prevents too much power being handed to any one institution or demographic. And forcing national candidates to appeal to a multitude of states rather than a handful of cities has a moderating effect.
If anything, what's outdated about the EC is we need more checks on the political influence that is centralized in cities because the founders didn't anticipate urbanization.
The first trouble is that you are opening the doors to all sorts of extremists. Whether they're far left, far right or simply personally unhinged, they are as predatory as spammers. They have no qualms about abusing your good will and s**ing in the punch bowl.
Same thing happened with Gab and Minds, but also smaller bulletin boards I've seen over the years. You set up a free speech site, and next thing you have a bunch of Nazis festooning it with swastikas. They're just like spammers, except they're spamming ideology instead of work from home scams.
The second trouble is that Parler is a competitor to established media corporations, who have a strong incentive to shut them down. That's a reality that every new business needs to face: established firms do not want competition.
All the people running amok provide the established firms with plenty of ammo to tar the upstart's reputation. And while they are biased by their incentives, they feel they are acting justly because they see all the extremist lunatics there. AWS didn't fabricate those incidents on Parler, all those were real posts.
Uncivil Law is a friend of theirs and he goes over the contract here: https://www.youtube.com/watch?v=PhWDKx9myI0&t=6187s
He's a conservative lawyer, so he doesn't have a political bias against Parler. They just don't have a case. I think Parler's lawyers misread the contract and thought the 30-day notice applied to termination for cause.
Disclaimer: worked at AWS.
Yes, Amazon has a vast number of engineers. They also have a vast need for engineers.
So they have to make a business case to continue to fund engineers, and I don't think one exists for the type of development we're wanting.
To be clear, there is some development they could justify.
If they are replicating new features that Elastic releases, so they're not falling behind the market, the business case is retaining customers. But that's playing catch up.
If new features optimize the service, the business case is being able to advertise lower TCO, which attracts customers. That could be quite beneficial, but there's usually not a ton of optimization you can do.
There is a business case for new features that increase integration with other AWS services. That increases customer usage of AWS. But AWS is so proprietary this would likely be useless outside AWS.
So none of that, I think, is the development people are looking for.
What we'd want are features a software business develops to differentiate their product in the market. If the features go into the open source version, however, then by definition they aren't differentiating.
This is the key business contradiction in AWS trying to fund open source development.
Maybe I'm missing something and they can make the business case for continued differentiating feature development. So, I'd say I'm wrong if after two years we're seeing such features being released to the commnuity.
Classic regex syntax has a similar problem as purely expression-based languages: it's hard to clearly delineate between smaller and larger scale structures.
Sometimes, brevity is clarity. It's bad to write matchDigit().times(2).match('-').matchDigit().times(2) instead of simply '\d\d-\d\d'. So even with the problems with metacharacters, I don't think you want to lose that.
But more complex regexes are clearest when the individual parts are assigned to meaningful names and they're then composed into the final expression.
Most regex implementations require that you're doing string-munging to compose regular expressions, and developers and maintainers must be aware of the semantics of that string-munging to the regex compiler.
Allowing the dev to compose regular expressions from parts, then, seems like the greatest opportunity here for improving regex syntax. It'd be especially helpful for any dynamically generated regular expression, and you could have facilities like a "quote" operator.
For instance, suppose you're doing a lot of date arithmetic, and you might include times. If I have a range:
[2005-05-05, 2007-07-07)
This unambiguously includes all times. If I compose it: [2005-05-05, 2007-07-07) [2007-07-07, 2008-08-08)
If I shift all of them forward 10 days: [2005-05-15, 2007-07-17) [2007-07-17, 2008-08-18)
Every possible time within the new range is accounted for. Transformations on the ranges are obviously correct. If we can't agree on whether time resolves to seconds, milliseconds or nanoseconds, it doesn't matter.That's because you can find the beginning and end of a range without knowing how to subtract by one. If you later decide to add times, it will just work because, conceptually, you already handle every possible value in the range.
If you want to construct a series of ranges of floats, it's a bad idea to do [3.457, 6.799999], [6.8, 12.47699999]. If you use half-open intervals, it just works, without mucking with the end of the range.
This is the reason that "indices are offsets" is powerful: it's not circular reasoning, but a simplification because we're removing an unnecessary primitive (subtracct 1) that only reliably works with integers.
And to bring this back to integer offsets, even when you're working strictly with integers, you may want to conceptually work with rational numbers.
When you're working out your math for buffered IO, you need to map byte indices to chunk indices. You need to assign every byte written to a chunk.
In a 0-based world, your chunk offset is simply your byte offset / chunk length. Because there is no distinction between indices and offsets, it's a clean mapping between one offset scheme and the other.
It means you can work out on paper when a new chunk should be started, and then translate that into integer arithmetic. And your code directly reflects the math, rather than having to stick "- 1" and "+ 1" in here and there.