James Reads


Day of Sep 16th, 2019

  • swyx Writing | You Already Use Types

    Published on Freecodecamp This post is for skeptics and newcomers to type systems, and aims to articulate rather than hard sell. First we'll look at how static type conventions appear in your dynamically typed coding. Then we'll step back and try to think about what this phenomenon tells us about how we want to code. Lastly, we'll ask some (leading!) questions that should arise from these insights. 1A: Types in Names Regardless of language, your journey with types starts almost as soon as you learn to code. The basic list data structure invites a corresponding plural: var dog = 'Fido' var dogs = ['Fido', 'Sudo', 'Woof'] As you work with more and more and more code, you start to form opinions that you may mandate to your team or style guide: always use specific names like dogID vs dogName vs dogBreed or a namespace/class/object like dog.name or dog.id or dog.breed singles should not be substrings of plurals, e.g. BAD: blog and blogs, GOOD: blogPost vs blogList booleans should have a boolean-ish prefix, like isLoading, hasProperty, didChange functions with side effects should have verbs internal variables should have a _prefix This may seem trivial since we're talking about variable names, but this vein runs extremely deep. Names in our coding reflect the concepts and constraints we place on our code to make it more maintainable at scale: These all seep into your code accordingly: *Container, *Component, *Reducer, *Template, *Page, with*. Once you start crossing execution paradigms, you start feeling your way into monadic type hints. Node.js felt this early on: fs.readFile(myfile, callback) fs.readFileSync(myfile) // introduced when people realized callback hell might not be worth non-blocking React introduced the use prefix to indicate hooking into the runtime that must respect certain rules: function Component() { const [bool, setBool] = React.useState(true) React.useEffect(callback) const foo = useCustomHook() // ... } I am personally fond of reminders of nullability: const maybeResult = await fetchAPI() if (maybeResult) { const result = maybeResult // do things with result } else { // maybeResult is falsy, dont assume it is there } In almost everything you name, you're already using types. So what, you ask? Keep reading, I'm building up to it. 1B: Types in Data Structures The problem with encoding types in names is that the language probably doesn't care about your meticulously named variables (indeed, in JavaScript, it probably gets mercilessly minified beyond recognition). It will happily run your code and throw a runtime error if you forget to respect your own nametypehints. What if we made types formally checkable through data structures? The most basic are constants. In Redux, it is common to explicitly (and redundantly) set SCREAMING_CASE_CONSTANTS: const ADD_TODO = 'slice/ADD_TODO' // later in redux code: import { ADD_TODO } from './redux/types' switch (action.type) { case ADD_TODO: // do stuff based on the action // ... } This is mostly done because you can't trust your fellow developer not to typo their strings. However even these strings offer too much trust, and we found it important enough to add a new language feature to guarantee uniqueness: const ADD_TODO = Symbol('slice/ADD_TODO') We also fake our way toward enums this way: const colors = { BLUE: Symbol(1), GREEN: Symbol(2), RED: Symbol(3), } But simple values (strings, numbers, booleans) are actually easy to compare and treat accordingly. More pressing is encoding types in complex values. This usually happens when you have arrays of objects and the objects are different in some ways and similar in others: const animals = [{ name: 'Fido', legs: 4, says: 'woof' }, { name: 'Kermit', legs: 2, marriedTo: 'Piggy' }] // will have bugs if an animal with both `says` and `marriedTo` exists animals.forEach((animal) => { if (animal.says) { // i guess it's a dog? } if (animal.marriedTo) { // i guess it's a frog? } }) Buggy checking and implicitly assumed types is often a cause for much pain. Better to type explicitly: const animals = [ { type: 'dog', // new! name: 'Fido', legs: 4, says: 'woof', }, { type: 'frog', // new! name: 'Kermit', legs: 2, marriedTo: 'Piggy', }, ] animals.forEach((animal) => { if (animal.type === 'dog') { // must be a dog! } if (animal.type === 'frog') { // must be a frog! } }) This is in fact what happens for Redux (and, interestingly enough, handy for other things like Discriminated Unions), but you will see this everywhere in Gatsby and Babel and React and I'm sure you know of cases I don't. Types even exist in HTML: <input type="file"> and <input type="checkbox"> behave so differently! (and I already mentioned Types in CSS with Block__Element--Modifier) Even in HTML/CSS, you're already using types. 1C: Types in APIs I'm almost done. Even outside your programming language, the interfaces between machines involve types. REST's big innovation was basically a primitive form of typing client-server requests: GET, PUT, POST, DELETE. Web conventions have introduced other type fields in requests, like the accept-encoding header, that you must adhere to to get what you want. However, RESTfulness is basically not enforced, and because it doesn't offer guarantees, downstream tooling cannot assume properly behaved endpoints. GraphQL takes that idea and dials it up to 11: Types are key to queries and mutations and fragments, but also on every field and every input variable, validated on both clientside and serverside by spec. With much stronger guarantees, it is able to ship much better tooling as a community norm. I don't know the history of SOAP and XML and gRPC and other machine-machine communication protocols but I'm willing to bet there are strong parallels. Part 2: What Does This Tell Us? This was a very long, and yet inexhaustive examination of types permeating everything you do. Now that you've seen these patterns, you can probably think of more examples I'm forgetting right now. But at every turn, it seems the way toward more maintainable code, and better tooling is to add types in some way. I mentioned parts of this thesis in How To Name Things, but basically all of the naming schemas fall under an enlightened form of Hungarian notation, as described in Joel Spolsky's Making Wrong Code Look Wrong. If none of what I have described resonates with you, and isn't something you've already been doing, then types may not be for you. But if it does, and you've been doing this in slipshod fashion, you may be interested in more structure around how you use types in your code, and in using better tooling that takes advantage of all the hard work you already put into types. You may be working your way toward a type system, without even knowing it. Part 3: Leading Questions So knowing what we know now about using types in our code without a type system. I'll ask some hard questions. Question 1: What do you currently do to enforce types without a type system? At an individual level, you engage in defensive coding and manual verification. Basically manually eyeballing your own code and reflexively adding checks and guards without knowing if they're really needed (or, worse, NOT doing it and figuring out after seeing run time exceptions). At a team level, you spend multiples of developer-hours in code review, inviting bike shedding over names, which we all know is great fun. These two processes are manual methods, and a very poor use of developer time. Don't be the bad cop - this wrecks team dynamics. At scale, you are mathematically guaranteed to have lapses in code quality (therefore causing production bugs), either because everyone missed something, or there just wasn't enough time and you just had to ship something, or there wasn't a good enough policy in place yet. The solution, of course, is to automate it. As Nick Schrock says, Delegate to Tooling Whenever Possible. Prettier and ESLint help to hold up your code quality - only to the extent to which the program can understand you based on an AST. It does not offer any help crossing function and file boundaries - if function Foo expects 4 arguments and you only pass it 3, no linter will yell at you and you'll have to defensively code inside Foo. So there's only so much you can automate with a linter. What about the rest you can't automate? Therein lies the last option: Do Nothing. Most people do nothing to enforce their informally designed type systems. Question 2: How much of these types are you writing yourself? It goes without saying that if all your type policies are created by you, then they must be written by you and enforced by you. That's totally different from how we write code today. We lean heavily on open source - 97% of modern web app code is from npm. We import shared code, and then write the last mile parts that make our app special (aka business logic). Is there a way to share types? (yes) Question 3: What if your types were standardized? Research has shown that the #1 reason programmers adopt a language is the existing capabilities and functionality available for them to use. I will learn Python to use TensorFlow. I will learn Objective C to create native iOS experiences. Correspondingly, JS has been so successful because it runs everywhere, compounded by the wide availability of free open source software written by other people. With some standardized type system, we can import types just as easily as we import open source software written by other people. Just like GraphQL vs REST, Standardized types in a language unlock much better tooling. I will offer 4 examples: Example 1: Faster Feedback We might take months and days to learn from runtime errors, and these are exposed to users, so they are the worst possible outcome. We write tests and apply lint rules and other checks to move these errors to build time errors, which shortens feedback cycles to minutes and hours. (As I wrote recently: Types don't replace Tests!) Type Systems can shorten this feedback by yet another order of magnitude, to seconds, checking during write time. (Linters can also do this. Both are conditional on a supportive IDE like VS Code) As side effect, you get autocomplete for free, because autocomplete and write time validation are two sides of the same coin. Example 2: Better Error Messages const Foo = { getData() { return 'data' }, } Foo['getdata']() // Error: undefined is not a function JavaScript is intentionally lazy evaluation by design. Instead of the dreaded and nondescript undefined is not a function during runtime, we can move this to write time. Here's the write time error message for the exact same code: const Foo = { getData() { return 'data' }, } Foo['getdata']() // Property 'getdata' does not exist on type '{ getData(): string; }'. Did you mean 'getData'? Why yes, TypeScript, I did. Example 3: Edge Case Exhaustion let fruit: string | undefined fruit.toLowerCase() // Error: Object is possibly 'undefined'. Over and above the built in nullable checking (which takes care of issues like passing in 3 arguments when a function expects 4), a type system can make the most of your enums (aka union types). I struggled coming up with a good example but here is one: type Fruit = 'banana' | 'orange' | 'apple' function makeDessert(fruit: Fruit) { // Error: Not all code paths return a value. switch (fruit) { case 'banana': return 'Banana Shake' case 'orange': return 'Orange Juice' } } Example 4: Fearless Refactoring Many people mentioned this and I'll be honest that it took me a long while to come around to this. The thinking is: "so what? I don't refactor that much. so that means TypeScript's benefit is smaller to me than to you because I'm better than you." This is the wrong take. When we start off exploring a problem, we start off with a vague idea of the solution. As we progress, we learn more about the problem, or priorities change, and unless we've done it a million times we probably picked something wrong along the way, whether it be function API, data structure, or something larger scale. The question is then to either stick with it until it breaks or to refactor the moment you can sense that you're going to outgrow whatever you used to have. I'll assume you accept that there are often benefits to refactoring. So why do we avoid refactoring? The reason you put off that refactor is that it is costly, not because it isn't beneficial to you. Yet putting it off only increases future cost. Type System tooling helps to dramatically lower the cost of that refactor, so you can experience the benefits earlier. It lowers that cost via faster feedback, exhaustiveness checking, and better error messages. Truth in Advertising There is a cost to learning Type Systems you didn't write. This cost may offset any imagined benefit to automated type checking. This is why I put a great deal of effort into helping to lower that learning curve. However, be aware that it is a new language and will involve unfamiliar concepts, and also that even the tooling is an imperfect work in progress. But it is good enough for AirBnb and Google and Atlassian and Lyft and Priceline and Slack and it may be for you. Structure of this post: - Hungarian-lite - isLoading - _internal vars - Plurals - posts - postIndex - Naming Grammar - *Container, *Component - Atoms, Molecules, Organisms, Templates, Pages - Monads - use* - sync* - maybe* - form effects - {type: 'foo'} - - ENUMS and Symbols - APIs - REST: GET PUT POST DELETE - GraphQL: types all the way down - Big Brain Time - Joel Spolsky's [Making Wrong Code Look Wrong](https://www.joelonsoftware.com/2005/05/11/making-wrong-code-look-wrong/) - How to Name Things - Questions to ask: - What do we do to enforce types without a type system? - Linting - Code Review - Defensive coding - Nothing - How much of these types are you writing yourself? What if your types were standardized? - better error messages - autocomplete and write time errors > build time errors > run time errors - Other people can write them for you - edge case exhaustion Source: swyx Writing | You Already Use Types

    Read at 03:59 pm, Sep 16th

  • Google feedback on TypeScript 3.5 · Issue #33272 · microsoft/TypeScript

    We recently upgraded Google to use TypeScript 3.5. Here is some feedback on the upgrade. (For background, recall that Google is a monorepo of billions of lines of code. We use a single version of TypeScript and a single set of compiler flags across all teams and upgrade these simultaneously for everyone.) We know and expect every TypeScript upgrade to involve some work. For example, improvements to the standard library are expected and welcomed by us, even though they may mean removing similar but incompatible definitions from our own code base. However, TypeScript 3.5 was a lot more work for us than other recent TypeScript upgrades. There were three main changes in 3.5 that made it especially painful. (The other changes were also required work, but these three are worth some extra discussion.) We believe most of these changes were intentional and intended to improve type checking, but we also believe the TypeScript team understands that type checking is always just a tradeoff between safety and ergonomics. It is our hope that this report about TS 3.5 as applied to a large codebase will help the TypeScript team better evaluate future situations that are similar, and we make some recommendations. Implicit default for generics This was the headline breaking change in 3.5. We agree with the end goal of this change, and understand that it will shake up user code. Historically when TypeScript has introduced type system changes like this, they were behind a flag. Suggestion: Using a flag here would have allowed us to adapt to this change separately from the other breaking changes in 3.5. The main way this failed is in circumstances where code had a generic that was irrelevant to what the code did. For example, consider some code that has a Promise resolve, but doesn't care about what value the Promise to resolves to: function dontCarePromise() { return new Promise((resolve) => { resolve(); }); } Because the generic is unbound, under 3.4 this was Promise<{}> and under 3.5 this becomes Promise<unknown>. If a user of this function wrote down the type of that promise anywhere, e.g.: const myPromise: Promise<{}> = dontCarePromise(); it now became a type error for no user benefit. The bulk of churn from this generics change was in code like this, where someone wrote a {} mostly because it was what the compiler said without really caring what type it was. One common concrete example of this don't-care pattern are the typings for the d3 library, which has a very complex d3.Selection<> that takes four generic arguments. In the vast majority of use cases the last two are irrelevant, but any time someone saves a Selection into a member variable, they ended up writing down whatever type TS inferred at that time, e.g.: mySel: d3.Selection<HTMLElement, {}, null, undefined>; The 3.5 generics change means that {} became unknown simultaneously in almost every interaction with d3. Suggestion: Our main conclusion about specifically d3 is that the d3 typings are not great and need some attention. There are some other type-level issues with them (outside of this upgrade) that I'd like to go into more, but it's not relevant to this upgrade. Another troublesome pattern are what we call "return-only generics", which is any pattern where a generic function only uses it in the return type. I think the TypeScript team already knows how problematic these are, with lots of inference surprises. For example, in the presence of a return only generic, the code: expectsString(myFunction()); can be legal while the innocent-looking refactor const x = myFunction(); expectsString(x); can then fail. Suggestion: We'd be interested in seeing whether TypeScript could compile-fail on this pattern entirely, rather than picking a top type ({} or unknown). Users are happy to specify the generic type at the call site, e.g. myFunction<string>() but right now the compiler doesn't help them see when they need it. For example, maybe the declaration myFunction<T>(...) could always require a specific T to be inferred, because you can always write myFunction<T=unknown>() for the case where you are ok with a default. One other common cause of return-only generics is a dependency injection pattern. Consider some test framework that provides some sort of injector function: function getService<T>(service: Ctor<T>): T; where Ctor<T> is some type that matches class values. The intended use of this is e.g. class MyService { … } const myService = getService(MyService); This works great up until MyService is generic, at which point this again picks an arbitrary <T> for the return type. The problem here is that we pass the MyService value to getService, but then we get back the MyService type, which needs a generic. One last source of return-only generics that we discovered is that the generic doesn't need to be in the return type. See the next section. filter(Boolean) TypeScript 3.5 changed the type of the Boolean function, which coerces a value to boolean, from (effectively) function Boolean(value?: any): boolean; to function Boolean<T>(value?: T): boolean; These look like they might behave very similarly. But imagine a function that takes a predicate and returns an array filter, and using it with the above: function filter<T>(predicate: (t: T) => boolean): (ts: T[]) => T[]; const myFilter = filter(Boolean); With the 3.4 definition of Boolean, T is pinned to any and myFilter becomes a function from any[] to any[]. With the 3.5 definition, T remains generic. We believe this change was intentional, to improve scenarios like this. The RxJS library uses a more complex variant of the above pattern, and a common use of it creates a function composition pipeline with a filter(Boolean) much like the above. With TS 3.4, users were accidentally getting any downstream of that point. With TS 3.5, they instead get a generic T that then feeds into a larger inference. You can read the full RxJS bug for some more context. One of the big surprises here is that everyone using RxJS was getting an unexpected any at this point. We knew to look for any in return types, but now we know that even if you accept an any in an argument type, via inference this can cause other types to become any. Suggestion: A more sophisticated definition of Boolean, one that removed null|undefined from its generic, might have helped, but from our experiments in this area there are further surprises (search for "backwards" on the above RxJS bug). This was also not mentioned in the list of breaking changes in 3.5. It's possible its impact was underestimated because it disproportionately affects RxJS (and Angular) users. Set In TypeScript 3.4, const s = new Set(); gave you back a Set<any>. (It's actually a kind of amusing type, because in some sense almost everything still works as expected -- you can put stuff in the set, .has() will tell you whether something is in the set, and so on. I suspect this might be why nobody noticed.) TypeScript 3.5 made a change in lib.es2015.iterable.d.ts that had the effect of removing the any, and the generic change described above made it now infer unknown. This change ended up being tedious to fix, because the eventual type errors sometimes were pretty far from the actual problem. For example, in this code: class C { gather() { let s = new Set(); s.add('hello'); return s; } use(s: string[]) { … } demo() { this.use(Array.from(this.gather())); } } You get a type error down by the Array.from but the required fix is at the new Set(). (I might suggest the underlying problem in this code is relying on inference too much, but the threshold for "too much" is difficult to communicate to users.) Suggestion: we are surprised nobody noticed this, since it broke our code everywhere. The only thing worth calling out here is that it seems like nobody made this change intentionally -- it's not in the breaking changes page, and the bug I filed about it seems to mostly have prompted confusion. The actual change that I think changed what overloads got picked looks harmless. Perhaps the main lesson we learned here is that we needed to discover this earlier and provide this feedback earlier. PS: It also appears new Map() may have the same problem with any. Conclusion I'd like to emphasize we are very happy with TypeScript in general. It is our hope that the above critical feedback is useful to you in your design process for future development of TypeScript. Source: Google feedback on TypeScript 3.5 · Issue #33272 · microsoft/TypeScript

    Read at 03:06 pm, Sep 16th

  • void in JavaScript and TypeScript

    void in JavaScript and TypeScript 06 September 2019 by @ddprrt | Posted in: TypeScript, JavaScript If you come from traditional, strongly typed languages you might be familiar with the concept of void: A type telling you that functions and methods return nothing when called. void exists in both JavaScript as an operator and in TypeScript as a primitive type. And in both worlds void works a little bit different than most people are used to. void in JavaScript is an operator which evaluates the expression next to it. No matter which expression is evaluated, void always returns undefined. Why would we need something like this? First, in earlier times, people were able to override undefined and giving it an actual value. void always returned the real undefined. Second, it’s a nice way to call immediately invoked functions: All without polluting the global namespace: Since void always returns undefined, and void always evaluates the expression next to it, you have a very terse way of returning from a function without returning a value, but still calling a callback for example: Which brings me to the most important use case of void: It’s a security gate for your app. When your function is always supposed to return undefined, you can make sure that this is always the case. void in TypeScript is a subtype of undefined. Functions in JavaScript always return something. Either it’s a value, or undefined: Since functions without a return value always return undefined, and void always returns undefined in JavaScript, void in TypeScript is a proper type for telling developers that this function returns undefined: void as type can also be used for parameters and all other declarations. The only value that can be passed is undefined: So void and undefined are pretty much the same. There’s one little difference though, and this difference is significant: void as a return type can be substituted with different types, to allow for advanced callback patterns: This is desired behaviour and often used in JavaScript applications. Read more on this pattern called substitutability in my other articles. If you want to make sure to pass functions who only return undefined (as in “nothing”), be sure to adapt your callback method signature: - function doSomething(callback: () => void) { + function doSomething(callback: () => undefined) { /* ... */ } function aNumberCallback(): number { return 2; } // 💥 types don't match doSomething(aNumberCallback) You’ll propably be good with void most of the time. More articles about TypeScript Comments? Shoot me a tweet! Source: void in JavaScript and TypeScript

    Read at 02:47 pm, Sep 16th

  • Stop using Knex.js

    This is true about any SQL query builder. I chose to use knex.js as an example because it is the most popular SQL query builder in the Node.js ecosystem and we need an example.

    Read at 01:11 pm, Sep 16th

  • The Myths of the “Genius” Behind Trump’s Reelection Campaign

    This article was co-published with Texas Monthly. ProPublica is a nonprofit newsroom that investigates abuses of power. Sign up for ProPublica’s Big Story newsletter to receive stories like this one in your inbox as soon as they are published.

    Read at 12:38 pm, Sep 16th

  • NYC socialists set their sights on City Council seats

    Congress, Albany… is City Hall next? NYC’s democratic socialists are planning to field candidates for City Council races in 2021, hoping to ride the momentum from Rep. Alexandria Ocasio-Cortez’s upset 2018 victory and local races like the one that swept state Sen. Julia Salazar into office.

    Read at 03:18 am, Sep 16th

  • Don’t Trust Elizabeth Warren’s Big-Donor Ban

    Warren was courting megadonors last year, and says she’ll do it again if she wins the primary. But working people deserve leaders willing to make enemies in high places — for life.

    Read at 03:15 am, Sep 16th

  • Unpaid labor

    In Birth Strike, Jenny Brown argues for an analysis of the politics of reproductive rights that is rooted not in religious or moral concerns, but in economics—specifically, the economic power of women’s unpaid labor.

    Read at 02:09 am, Sep 16th

  • Community WiFi is Already Here, Now It's Time to Expand It

    While politicians and industry focus on 5G wireless, the democratization of more common wireless technology has quietly made community-run wifi networks faster, better and cheaper than cable.

    Read at 02:06 am, Sep 16th

  • Just Upgrade: How PostgreSQL 12 Can Improve Your Performance

    PostgreSQL 12, the latest version of the "world's most advanced open source relational database," is being released in the next few weeks, barring any setbacks.

    Read at 02:06 am, Sep 16th

  • Alexandria Ocasio-Cortez Is on the 2019 TIME 100 List | Time.com

    react-text: 1180 Alexandria Ocasio-Cortez /react-text react-text: 1182 By /react-text react-text: 1183 Elizabeth Warren /react-text ► Watch Collier Schorr for TIME The year 2008 was a reckoning. While millions of Americans lost their livelihoods to Wall Street’s greed, Alexandria Ocasio-Cortez lost her dad to lung cancer, and her family fell off a financial cliff. She watched as our government bailed out Wall Street while it ignored families like hers. She learned the hard way that in America today, Washington protects the powerful while leaving hardworking people behind. Her commitment to putting power in the hands of the people is forged in fire. Coming from a family in crisis and graduating from school with a mountain of debt, she fought back against a rigged system and emerged as a fearless leader in a movement committed to demonstrating what an economy, a planet and a government that works for everyone should look like. A year ago, she was taking orders across a bar. Today, millions are taking cues from her. She reminds all of us that even while greed and corruption slow our progress, even while armies of lobbyists swarm Washington, in our democracy, true power still rests with the people. And she’s just getting started. Warren, a Senator from Massachusetts, is a Democratic presidential candidate Source: Alexandria Ocasio-Cortez Is on the 2019 TIME 100 List | Time.com

    Read at 12:20 am, Sep 16th

Day of Sep 15th, 2019

Day of Sep 14th, 2019

  • Joe Biden's Incoherence on Healthcare Proved Julian Castro Right at the Democratic Debate

    HOUSTON—"Anyone who thinks you can retrain thousands of truck drivers," Andrew Yang said, as an exaltation of cameras and boom microphones descended on him, "hasn't been at a truck stop lately. That's why I'm so passionate about the freedom dividends.

    Read at 10:38 pm, Sep 14th

  • Coffee House

    As so often in the last three years, much of our political debate is ducking the central strategic questions and is obsessing, in increasingly hysterical fashion on all sides, about tactical ones. We face the most explosive political week for years, perhaps decades.

    Read at 10:09 pm, Sep 14th

  • Tech Choices I Regret at Spectrum

    👋 I am Max, the technical co-founder of Spectrum. Spectrum is an open source chat app for large online communities and was recently acquired by GitHub. We are a team of three with a predominantly frontend and design background and have worked on it for close to two years. With the benefit of hindsight, here are the technology choices I regret and the lessons I have learned. Regret 1: Not using react-native-web A big part of Spectrum's appeal is that the content is public and search-indexed, which is why we built the website before native apps. Although the search-indexing was a success, our users have been requesting a better mobile experience. We are building native apps now, but starting from scratch is time consuming. If we had used react-native-web to build the website, we could have reused the base components and built the native apps much faster! 🏎💨 On top of that, we should have also optimised the website for mobile first. A great mobile experience on desktop is bearable and only needs tweaking to work well. However, a desktop experience on mobile is annoying, no matter how great, and it has proven hard to make ours work well on devices of all sizes. Lesson 1: Building a good product is all about experimentation and momentum. Optimize for iteration speed and flexibility. Regret 2: Not using Next.js We needed server-side rendering for SEO purposes (client-side rendering does not cut it) but had already built a first version of the app with create-react-app. We thought about switching to Next.js, but I decided that reworking the routing and data fetching would be more effort than building our own server-side rendering server. Turns out, building your own production-ready SSR setup is tough. It takes a lot of work and it is difficult to provide a good experience, for both developers and users. Next.js offers an amazing development experience and fast performance out of the box, not to mention the great community and excellent documentation. I would use it in a heartbeat if we started over today (in fact, this website is built with Next.js 😍). Lesson 2: Use existing solutions for technological problems where possible, particularly ones you do not understand deeply. Regret 3: Using RethinkDB I chose RethinkDB as our primary data store mainly because of changefeeds. They allow you to listen to live updates on (almost) any query. I thought this would reduce complexity by avoiding a separate PubSub system for real-time functionality. Unfortunately, we have had a lot of troubles with RethinkDB. Since it is not widely used, there is little documentation and knowledge about operations. We have had many database outages and debugging them has often felt like shooting in the dark. It also turns out that changefeeds do not scale as well as we had expected. While we managed to work around it, we should not have had to. 😕 Nowadays, I would choose a more established database (Postgres?) and build a PubSub system on top. Lesson 3: Carefully choose core technologies that are hard to change later. Lesson 4: Prioritize community size and active maintenance, especially in unfamiliar territory. Regret 4: Using DraftJS and WYSIWYG editing Writing is one of the primary activities on Spectrum, so we wanted the experience to be great. I decided to replace our plaintext markdown input with a custom WYSIWYG editor based on Draft.js, which had recently been released by Facebook. Unfortunately it did not work out well. The editor is really buggy, even after months of work our users rightfully complain about it constantly. On top of that, the library makes up a majority of our JavaScript bundle size and the lack of cross-browser support means that we have to keep the plaintext input around as a fallback. 👎 Another framework might have worked better, but in reality we should have focused on more pressing features instead. I thought we needed WYSIWYG editing but did not validate it by talking to our users. Otherwise, we would have quickly realised that there was no need for it. Lesson 5: Deliberately assess cutting edge technologies, bias towards conservative choices. Lesson 6: Be open with your roadmap to learn about your users priorities. Takeaways Changing these decisions would not have made Spectrum a better product by itself. Yet, it would have saved us time and allowed us to spend more time experimenting. To summarize, here are the six lessons I am taking away for my next projects: Building a good product is all about experimentation. Optimize for iteration speed and flexibility. Use existing solutions for technological problems where possible, particularly ones you do not understand deeply. Carefully choose core technologies that are hard to change later. Prioritize community size and active maintenance, especially in unfamiliar territory. Deliberately assess cutting edge technologies, bias towards conservative choices. Be open with your roadmap to learn about your users priorities. Source: Tech Choices I Regret at Spectrum

    Read at 06:46 pm, Sep 14th

  • swyx Writing | The Case for the React Native Web Singularity

    Bottom line up front: There is a possible "React Native Web Singularity", when it starts being a better standalone choice for developing for the mobile web than react-dom. If this speculation comes true, this would be gamechanging. Context I spent a couple hours looking into React Native Web today. I want to preface this with the very important acknowledgements that I have no knowledge of the React team's plans, and I have not ever built anything with React Native Web so I am really just speculating and thinking out loud. ⚠️I REITERATE: I AM JUST SOME INTERNET RANDO SPEWING THOUGHTS, DO NOT IN ANY WAY TAKE WHAT I WRITE HERE AS ANY FORM OF OFFICIAL ROADMAP I just want to discuss why React Native Web is interesting to me and what I see as a neutral but interested outsider to the project. However I am aware that RNW is a moderately controversial project and if you have your mind made up on it, I suggest doing something else with your time than reading on. I mean it, please stop reading if you are just going to get mad whenever RNW is mentioned. Wew. Ok. Basic Facts on React Native Web If you are out of the loop on even knowing what RNW is, here are some resources I would recommend: Also worth noting that there was a (more ambitious!) experiment, React Native DOM, that has since gone on hiatus. ReactXP is another notable attempt I know nothing about, backed by MSFT. Because there is a lot of ground to cover, I will assume from here on that you have read and watched these basic things above. The main selling points that resonate with me are: There is one very big downside, which cannot be ignored: it drops all pretense of using APIs that look like normal webdev. No <div>s here. No Media Queries. And expect fiddling with alien RN ecosystems like Metro with fun problems with symlinks. Now, I also pay attention to incidental/leading indicators: As well as community proof points: My speculation: The React Native Web Singularity Now for the really useless part of this blogpost, where I speculate about the potential of RNW without even ever having used it. Yeah, I know. Humor me, contradict me, back me up, I'm just thinking aloud and would love your thoughts. Fact: Mobile is extremely important for web developers. I won't bother substantiating this. Fact: A React-Native-informed approach to Gestures, Focus, and A11y is clearly in the future of React. Fact: It is working for Twitter and Uber Eats and others. Opinion: I don't think react-dom can or should ever be deprecated, because it will forever be important for web developers coming into React from a HTML/CSS background. I used to entertain this notion, but I recognize it is unnecessarily aggressive. Opinion: Code reuse is very project dependent. Plenty of projects are web-only. BUT... Speculation: There is a decent chance that RNW reaches a crossover point, a singularity, where it simply starts to be a better starting point for developing for mobile web than react-dom is. As a standalone tool, even with no intention of reusing code for native apps, it could be better than react-dom, for reasons I have already stated. At this point, all doubt of premature optimization becomes invalid, and RNW becomes the default for people who are ok not using HTML-like APIs. If true, THIS WOULD BE INCREDIBLY FREAKING IMPORTANT. This is why I keep eyeing the RNW project despite never having needed it. If true, This would be a fun turn of events from people proclaiming the death of RN after Airbnb sunset RN in 2018. If anything, judging by Eli White's talk, the expanded RN team with Lean Core/community focus, and open sourcing of Hermes, RN has in the public eye become more relevant than ever in 2019. It also doesn't have to be RN that "wins". If Flutter is a success, Bruno Lemos also notes that Flutter for web is in technical preview. Definitely Not Immediate Future I think all this is years out. I directly pointed this question at Dan a few months ago and this was his reply (which, again, is not official communication they have committed to so dont hold them to it please): Me: does some combination of React Native DOM/Web, React Fire, and React Flare point towards some possible future version of react-dom where we drop the HTML-like basic JSX components and just provide a smaller set that are more accessible/intuitive by default? Dan: Not in immediate feature. But finding ways to steer people towards accessible UIs by default is an active exploration with Flare. In shorter term insights will likely feed back into React Native. Maybe someday having a unified opt-in API for folks who prefer it would be nice. So RN is the near term focus for now. But what if... Feedback from early reviewers I was a bit too web centric in this piece, and was totally blind to the obvious fact that RNW is a bigger win for RN users than for React-Dom users. Glen Maddern has ported an RN app to RNW which of course is going to be a great usecase for RNW. He said: In general RNW isn't the bottleneck, it's the native bits of RN that cause trouble. So I would speculate that one day maybe Expo becomes better for building for the web than Create React App, that'll be the tipping point. RNW is a small piece of the puzzle but it feels good enough for now. The RNW DX story itself does have some drawbacks. Ryan Jerue says: In terms of using RNW for a bit over a year, I'd say that there's some good and some bad. The good is that it does deliver on its promise. My focus has been working on a design system that my company can use across our web and native apps. If you like CSS in JS, it's 100% that. Dimensions API covers media queries well too. The bad of RNW is mostly from react native, meaning webapps are going to be stuck at the ceiling of being annoying up upgrade, flexbox everywhere. The support of libraries isn't as great either as often ones with native modules require one to be skilled in JS, Swift/ObjC, and Java. React Native doesn't support SVG out of the box (or really at all) either, which is a bummer. Even if it isn't RNW, something else that looks like it might do it. Christian Hall said: Given the interest in VR and AR, for devs who want to focus on making the best UI possible, regardless of platform, I think the “RNW Singularity” is essential. If the React Team doesn’t come up with some way to express UI universally, someone else will. Bruno Lemos agreed: We are definitely trending into more cross-platform dev, all big companies are betting on it (facebook, microsoft, google and even apple with project catalina). Dunno if it will be react-native-web or not, but yes I agree with the predictions There remain key sticking points in a RNW singularity, like opening in a new tab, and routing. As Josh Parret put it: I feel we are quite far off a RNW singularity, as there’s undeniable cases where a single codebase doesn’t work for both platforms; such as opening a link in a new tab, or changing routes/screens... PostScript: more resources for my reference Source: swyx Writing | The Case for the React Native Web Singularity

    Read at 06:37 pm, Sep 14th

  • Reactive Search 3.0 — UI Components for building ✨ Search UIs

    Why have I been blocked? This website is using a security service to protect itself from online attacks. The action you just performed triggered the security solution. There are several actions that could trigger this block including submitting a certain word or phrase, a SQL command or malformed data. Source: Reactive Search 3.0 — UI Components for building ✨ Search UIs

    Read at 06:28 pm, Sep 14th

  • Model-Based Testing in React with State Machines | CSS-Tricks

    Testing applications is crucially important to ensuring that the code is error-free and the logic requirements are met. However, writing tests manually is tedious and prone to human bias and error. Furthermore, maintenance can be a nightmare, especially when features are added or business logic is changed. We’ll learn how model-based testing can eliminate the need to manually write integration and end-to-end tests, by automatically generating full tests that keep up-to-date with an abstract model for any app. From unit tests to integration tests, end-to-end tests, and more, there are many different testing methods that are important in the development of non-trivial software applications. They all share a common goal, but at different levels: ensure that when anyone uses the application, it behaves exactly as expected without any unintended states, bugs, or worse, crashes. Testing Trophy showing importance of different types of tests Kent C. Dodds describes the practical importance of writing these tests in his article, Write tests. Not too many. Mostly integration. Some tests, like static and unit tests, are easy to author, but don't completely ensure that every unit will work together. Other tests, like integration and end-to-end (E2E) tests, take more time to author, but give you much more confidence that the application will work as the user expects, since they replicate scenarios similar to how a user would use the application in real life. So why are there never many integration nor E2E in applications nowadays, yet hundreds (if not thousands) of unit tests? The reasons range from not enough resources, not enough time, or not enough understanding of the importance of writing these tests. Furthermore, even if numerous integration/E2E tests are written, if one part of the application changes, most of those long and complicated tests need to be rewritten, and new tests need to be written. Under deadlines, this quickly becomes infeasible. From Automated to Autogenerated The status-quo of application testing is: Manual testing, where no automated tests exist, and app features and user flows are tested manually Writing automated tests, which are scripted tests that can be executed automatically by a program, instead of being manually tested by a human Test automation, which is the strategy for executing these automated tests in the development cycle. Needless to say, test automation saves a lot of time in executing the tests, but the tests still need to be manually written. It would sure be nice to tell some sort of tool: "Here is a description of how the application is supposed to behave. Now generate all the tests, even the edge cases." Thankfully, this idea already exists (and has been researched for decades), and it's called model-based testing. Here's how it works: An abstract "model" that describes the behavior of your application (in the form of a directed graph) is created Test paths are generated from the directed graph Each "step" in the test path is mapped to a test that can be executed on the application. Each integration and E2E test is essentially a series of steps that alternate between: Verify that the application looks correct (a state) Simulate some action (to produce an event) Verify that the application looks right after the action (another state) If you’re familiar with the given-when-then style of behavioral testing, this will look familiar: Given some initial state (precondition) When some action occurs (behavior) Then some new state is expected (postcondition). A model can describe all the possible states and events, and automatically generate the "paths" needed to get from one state to another, just like Google Maps can generate the possible routes between one location and another. Just like a map route, each path is a collection of steps needed to get from point A to point B. Integration Testing Without a Model To better explain this, consider a simple "feedback" application. We can describe it like so: A panel appears asking the user, "How was your experience?" The user can click "Good" or "Bad" When the user clicks "Good," a screen saying "Thanks for your feedback" appears. When the user clicks "Bad," a form appears, asking for further information. The user can optionally fill out the form and submit the feedback. When the form is submitted, the thanks screen appears. The user can click "Close" or press the Escape key to close the feedback app on any screen. See the Pen Untitled by David Khourshid(@davidkpiano) on CodePen. Manually Testing the App The @testing-library/react library makes it straightforward to render React apps in a testing environment with its render() function. This returns useful methods, such as: getByText, which identifies DOM elements by the text contained inside of them baseElement, which represents the root document.documentElement and will be used to trigger a keyDown event queryByText, which will not throw an error if a DOM element containing the specified text is missing (so we can assert that nothing is rendered) import Feedback from './App'; import { render, fireEvent, cleanup } from 'react-testing-library'; // ... // Render the feedback app const { getByText, getByTitle, getByPlaceholderText, baseElement, queryByText } = render(<Feedback />); // ... More information can be found in the @testing-library/react documentation. Let's write a couple integration tests for this with Jest (or Mocha) and @testing-library/react: import { render, fireEvent, cleanup } from '@testing-library/react'; describe('feedback app', () => { afterEach(cleanup); it('should show the thanks screen when "Good" is clicked', () => { const { getByText } = render(<Feedback />); // The question screen should be visible at first assert.ok(getByText('How was your experience?')); // Click the "Good" button fireEvent.click(getByText('Good')); // Now the thanks screen should be visible assert.ok(getByText('Thanks for your feedback.')); }); it('should show the form screen when "Bad" is clicked', () => { const { getByText } = render(<Feedback />); // The question screen should be visible at first assert.ok(getByText('How was your experience?')); // Click the "Bad" button fireEvent.click(getByText('Bad')); // Now the form screen should be visible assert.ok(getByText('Care to tell us why?')); }); }); Not too bad, but you'll notice that there's some repetition going on. At first, this isn't a big deal (tests shouldn't necessarily be DRY), but these tests can become less maintainable when: Application behavior changes, such as adding a new steps or deleting steps User interface elements change, in a way that might not even be a simple component change (such as trading a button for a keyboard shortcut or gesture) Edge cases start occurring and need to be accounted for. Furthermore, E2E tests will test the exact same behavior (albeit in a more realistic testing environment, such as a live browser with Puppeteer or Selenium), yet they cannot reuse the same tests since the code for executing the tests is incompatible with those environments. The State Machine as an Abstract Model Remember the informal description of our feedback app above? We can translate that into a model that represents the different states, events, and transitions between states the app can be in; in other words, a finite state machine. A finite state machine is a representation of: The finite states in the app (e.g., question, form, thanks, closed) An initial state (e.g., question) The events that can occur in the app (e.g., CLICK_GOOD, CLICK_BAD for clicking the good/bad buttons, CLOSE for clicking the close button, and SUBMIT for submitting the form) Transitions, or how one state transitions to another state due to an event (e.g., when in the question state and the CLICK_GOOD action is performed, the user is now in the thanks state) Final states (e.g., closed), if applicable. The feedback app's behavior can be represented with these states, events, and transitions in a finite state machine, and looks like this: A visual representation can be generated from a JSON-like description of the state machine, using XState: import { Machine } from 'xstate'; const feedbackMachine = Machine({ id: 'feedback', initial: 'question', states: { question: { on: { CLICK_GOOD: 'thanks', CLICK_BAD: 'form', CLOSE: 'closed' } }, form: { on: { SUBMIT: 'thanks', CLOSE: 'closed' } }, thanks: { on: { CLOSE: 'closed' } }, closed: { type: 'final' } } }); If you're interested in diving deeper into XState, you can read the XState docs, or read a great article about using XState with React by Jon Bellah. Note that this finite state machine is used only for testing, and not in our actual application — this is an important principle of model-based testing, because it represents how the user expects the app to behave, and not its actual implementation details. The app doesn’t necessarily need to be created with finite state machines in mind (although it’s a very helpful practice). Creating a Test Model The app's behavior is now described as a directed graph, where the nodes are states and the edges (or arrows) are events that denote the transitions between states. We can use that state machine (the abstract representation of the behavior) to create a test model. The @xstate/graph library contains a createModel function to do that: import { Machine } from 'xstate'; import { createModel } from '@xstate/test'; const feedbackMachine = Machine({/* ... */}); const feedbackModel = createModel(feedbackMachine); This test model is an abstract model which represents the desired behavior of the system under test (SUT) — in this example, our app. With this testing model, test plans can be created which we can use to test that the SUT can reach each state in the model. A test plan describes the test paths that can be taken to reach a target state. Verifying States Right now, this model is a bit useless. It can generate test paths (as we’ll see in the next section) but to serve its purpose as a model for testing, we need to add a test for each of the states. The @xstate/test package will read these test functions from meta.test: const feedbackMachine = Machine({ id: 'feedback', initial: 'question', states: { question: { on: { CLICK_GOOD: 'thanks', CLICK_BAD: 'form', CLOSE: 'closed' }, meta: { // getByTestId, etc. will be passed into path.test(...) later. test: ({ getByTestId }) => { assert.ok(getByTestId('question-screen')); } } }, // ... etc. } }); Notice that these are the same assertions from the manually written tests we’ve created previously with @testing-library/react. The purpose of these tests is to verify the precondition that the SUT is in the given state before executing an event. Executing Events To make our test model complete, we need to make each of the events, such as CLICK_GOOD or CLOSE, “real” and executable. That is, we have to map these events to actual actions that will be executed in the SUT. The execution functions for each of these events are specified in createModel(…).withEvents(…): import { Machine } from 'xstate'; import { createModel } from '@xstate/test'; const feedbackMachine = Machine({/* ... */}); const feedbackModel = createModel(feedbackMachine) .withEvents({ // getByTestId, etc. will be passed into path.test(...) later. CLICK_GOOD: ({ getByText }) => { fireEvent.click(getByText('Good')); }, CLICK_BAD: ({ getByText }) => { fireEvent.click(getByText('Bad')); }, CLOSE: ({ getByTestId }) => { fireEvent.click(getByTestId('close-button')); }, SUBMIT: { exec: async ({ getByTestId }, event) => { fireEvent.change(getByTestId('response-input'), { target: { value: event.value } }); fireEvent.click(getByTestId('submit-button')); }, cases: [{ value: 'something' }, { value: '' }] } }); Notice that you can either specify each event as an execution function, or (in the case of SUBMIT) as an object with the execution function specified in exec and sample event cases specified in cases. From Model To Test Paths Take a look at the visualization again and follow the arrows, starting from the initial question state. You'll notice that there are many possible paths you can take to reach any other state. For example: From the question state, the CLICK_GOOD event transitions to... the form state, and then the SUBMIT event transitions to... the thanks state, and then the CLOSE event transitions to... the closed state. Since the app's behavior is a directed graph, we can generate all the possible simple paths or shortest paths from the initial state. A simple path is a path where no node is repeated. That is, we're assuming the user isn't going to visit a state more than once (although that might be a valid thing to test for in the future). A shortest path is the shortest of these simple paths. Rather than explaining algorithms for traversing graphs to find shortest paths (Vaidehi Joshi has great articles on graph traversal if you're interested in that), the test model we created with @xstate/test has a .getSimplePathPlans(…) method that generates test plans. Each test plan represents a target state and simple paths from the initial state to that target state. Each test path represents a series of steps to get to that target state, with each step including a state (precondition) and an event (action) that is executed after verifying that the app is in the state. For example, a single test plan can represent reaching the thanks state, and that test plan can have one or more paths for reaching that state, such as question -- CLICK_BAD → form -- SUBMIT → thanks, or question -- CLICK_GOOD → thanks: testPlans.forEach(plan => { describe(plan.description, () => { // ... }); }); We can then loop over these plans to describe each state. The plan.description is provided by @xstate/test, such as reaches state: "question": // Get test plans to all states via simple paths const testPlans = testModel.getSimplePathPlans(); And each path in plan.paths can be tested, also with a provided path.description like via CLICK_GOOD → CLOSE: testPlans.forEach(plan => { describe(plan.description, () => { // Do any cleanup work after testing each path afterEach(cleanup); plan.paths.forEach(path => { it(path.description, async () => { // Test setup const rendered = render(<Feedback />); // Test execution await path.test(rendered); }); }); }); }); Testing a path with path.test(…) involves: Verifying that the app is in some state of a path’s step Executing the action associated with the event of a path’s step Repeating 1. and 2. until there are no more steps Finally, verifying that the app is in the target plan.state. Finally, we want to ensure that each of the states in our test model were tested. When the tests are run, the test model keeps track of the tested states, and provides a testModel.testCoverage() function which will fail if not all states were covered: it('coverage', () => { testModel.testCoverage(); }); Overall, our test suite looks like this: import React from 'react'; import Feedback from './App'; import { Machine } from 'xstate'; import { render, fireEvent, cleanup } from '@testing-library/react'; import { assert } from 'chai'; import { createModel } from '@xstate/test'; describe('feedback app', () => { const feedbackMachine = Machine({/* ... */}); const testModel = createModel(feedbackMachine) .withEvents({/* ... */}); const testPlans = testModel.getSimplePathPlans(); testPlans.forEach(plan => { describe(plan.description, () => { afterEach(cleanup); plan.paths.forEach(path => { it(path.description, () => { const rendered = render(<Feedback />); return path.test(rendered); }); }); }); }); it('coverage', () => { testModel.testCoverage(); }); }); This might seem like a bit of setup, but manually scripted integration tests need to have all of this setup anyway, in a much less abstracted way. One of the major advantages of model-based testing is that you only need to set this up once, whether you have 10 tests or 1,000 tests generated. Running the Tests In create-react-app, the tests are ran using Jest via the command npm test (or yarn test). When the tests are ran, assuming they all pass, the output will look something like this: PASS src/App.test.js feedback app ✓ coverage reaches state: "question" ✓ via (44ms) reaches state: "thanks" ✓ via CLICK_GOOD (17ms) ✓ via CLICK_BAD → SUBMIT ({"value":"something"}) (13ms) reaches state: "closed" ✓ via CLICK_GOOD → CLOSE (6ms) ✓ via CLICK_BAD → SUBMIT ({"value":"something"}) → CLOSE (11ms) ✓ via CLICK_BAD → CLOSE (10ms) ✓ via CLOSE (4ms) reaches state: "form" ✓ via CLICK_BAD (5ms) Test Suites: 1 passed, 1 total Tests: 9 passed, 9 total Snapshots: 0 total Time: 2.834s That's nine tests automatically generated with our finite state machine model of the app! Every single one of those tests asserts that the app is in the correct state and that the proper actions are executed (and validated) to transition to the next state at each step, and finally asserts that the app is in the correct target state. These tests can quickly grow as your app gets more complex; for instance, if you add a back button to each screen or add some validation logic to the form page (please don't; be thankful the user is even going through the feedback form in the first place) or add a loading state submitting the form, the number of possible paths will increase. Advantages of Model-Based Testing Model-based testing greatly simplifies the creation of integration and E2E tests by autogenerating them based on a model (like a finite state machine), as demonstrated above. Since manually writing full tests is eliminated from the test creation process, adding or removing new features no longer becomes a test maintenance burden. The abstract model only needs to be updated, without touching any other part of the testing code. For example, if you want to add the feature that the form shows up whether the user clicks the "Good" or "Bad" button, it's a one-line change in the finite state machine: // ... question: { on: { // CLICK_GOOD: 'thanks', CLICK_GOOD: 'form', CLICK_BAD: 'form', CLOSE: 'closed', ESC: 'closed' }, meta: {/* ... */} }, // ... All tests that are affected by the new behavior will be updated. Test maintenance is reduced to maintaining the model, which saves time and prevents errors that can be made in manually updating tests. This has been shown to improve efficiency in both developing and testing production applications, especially as used at Microsoft on recent customer projects — when new features were added or changes made, the autogenerated tests gave immediate feedback on which parts of the app logic were affected, without needing to manually regression test various flows. Additionally, since the model is abstract and not tied to implementation details, the exact same model, as well as most of the testing code, can be used to author E2E tests. The only things that would change are the tests for verifying the state and the execution of the actions. For example, if you were using Puppeteer, you can update the state machine: // ... question: { on: { CLICK_GOOD: 'thanks', CLICK_BAD: 'form', CLOSE: 'closed' }, meta: { test: async (page) => { await page.waitFor('[data-testid="question-screen"]'); } } }, // ... const testModel = createModel(/* ... */) .withEvents({ CLICK_GOOD: async (page) => { const goodButton = await page.$('[data-testid="good-button"]'); await goodButton.click(); }, // ... }); And then these tests can be run against a live Chromium browser instance: The tests are autogenerated the same, and this cannot be overstated. Although it just seems like a fancy way to create DRY test code, it goes exponentially further than that — autogenerated tests can exhaustively represent paths that explore all the possible actions a user can do at all possible states in the app, which can readily expose edge-cases that you might not have even imagined. The code for both the integration tests with @testing-library/react and the E2E tests with Puppeteer can be found in the the XState test demo repository. Challenges to Model-Based Testing Since model-based testing shifts the work from manually writing tests to manually writing models, there is a learning curve. Creating the model necessitates the understanding of finite state machines, and possibly even statecharts. Learning these are greatly beneficial for more reasons than just testing, since finite state machines are one of the core principles of computer science, and statecharts make state machines more flexible and scalable for complex app and software development. The World of Statecharts by Erik Mogensen is a great resource for understanding and learning how statecharts work. Another issue is that the algorithm for traversing the finite state machine can generate exponentially many test paths. This can be considered a good problem to have, since every one of those paths represents a valid way that a user can potentially interact with an app. However, this can also be computationally expensive and result in semi-redundant tests that your team would rather skip to save testing time. There are also ways to limit these test paths, e.g., using shortest paths instead of simple paths, or by refactoring the model. Excessive tests can be a sign of an overly complex model (or even an overly complex app 😉). Write Fewer Tests! Modeling app behavior is not the easiest thing to do, but there are many benefits of representing your app as a declarative and abstract model, such as a finite state machine or a statechart. Even though the concept of model-based testing is over two decades old, it is still an evolving field. But with the above techniques, you can get started today and take advantage of generating integration and E2E tests instead of manually writing every single one of them. More resources: I gave a talk at React Rally 2019 demonstrating model-based testing in React apps: Slides: Slides: Write Fewer Tests! From Automation to Autogeneration Happy testing! Source: Model-Based Testing in React with State Machines | CSS-Tricks

    Read at 06:25 pm, Sep 14th

  • Easy functional programming techniques in TypeScript for everyone - DEV Community 👩‍💻👨‍💻

    There is a lot of hype around functional programming(FP) and a lot of cool kids are doing it but it is not a silver bullet. Like other programming paradigms/styles, functional programming also has its pros and cons and one may prefer one paradigm over the other. If you are a TypeScript/JavaScript developer and wants to venture into functional programming, do not worry, you don't have to learn functional programming oriented languages like Haskell or Clojure since JavaScript and hence TypeScript has you covered and this post is for you. If you are looking for functional programming in Java or Golang check other posts in the series. I'm not gonna dive into all functional programming concepts in detail, instead, I'm gonna focus on things that you can do in TypeScript which are in line with functional programming concepts. I'm also not gonna discuss the pros and cons of functional programming in general. Please keep in mind, though this post is about TypeScript, you can easily do the same in JavaScript as well since TypeScript is just a typed superset of JavaScript. What is functional programming? As per Wikipedia, Functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. Hence in functional programming, there are two very important rules No Data mutations: It means a data object should not be changed after it is created. No implicit state: Hidden/Implicit state should be avoided. In functional programming state is not eliminated, instead, its made visible and explicit This means: No side effects: A function or operation should not change any state outside of its functional scope. I.e, A function should only return a value to the invoker and should not affect any external state. This means programs are easier to understand. Pure functions only: Functional code is idempotent. A function should return values only based on the arguments passed and should not affect(side-effect) or depend on global state. Such functions always produce the same result for the same arguments. Apart from these there are functional programming concepts below that can be applied in TypeScript, we will touch upon these further down. Using functional programming doesn't mean its all or nothing, you can always use functional programming concepts to complement Object-oriented concepts in TypeScript. The benefits of functional programming can be utilized whenever possible regardless of the paradigm or language you use. And that is exactly what we are going to see. Functional programming in TypeScript TypeScript is not a purely functional language but offers a lot of concepts which are in line with functional languages, so let us see how we can apply some of the functional programming concepts above in TypeScript. First-class and higher-order functions First-class functions(function as a first-class citizen) means you can assign functions to variables, pass a function as an argument to another function or return a function from another. TypeScript supports this and hence makes concepts like closures, currying, and higher-order-functions easy to write. A function can be considered as a higher-order-function only if it takes one or more functions as parameters or if it returns another function as a result. In TypeScript, this is quite easy to do type mapFn = (it: string) => number; // The higher-order-function takes an array and a function as arguments function mapForEach(arr: string[], fn: mapFn): number[] { const newArray: number[] = []; arr.forEach(it => { // We are executing the method passed newArray.push(fn(it)); }); return newArray; } const list = ["Orange", "Apple", "Banana", "Grape"]; // we are passing the array and a function as arguments to mapForEach method. const out = mapForEach(list, (it: string): number => it.length); console.log(out); // [6, 5, 6, 5] But then in JavaScript/TypeScript we could also simply do it this way using built-in functional methods like map, reduce and so on. const list = ["Orange", "Apple", "Banana", "Grape"]; // we are passing a function as arguments to the built-in map method. const out = list.map(it => it.length); console.log(out); // [6, 5, 6, 5] Closures and currying are also possible in TypeScript // this is a higher-order-function that returns a function function add(x: number): (y: number) => number { // A function is returned here as closure // variable x is obtained from the outer scope of this method and memorized in the closure return (y: number): number => x + y; } // we are currying the add method to create more variations var add10 = add(10); var add20 = add(20); var add30 = add(30); console.log(add10(5)); // 15 console.log(add20(5)); // 25 console.log(add30(5)); // 35 There are also many built-in declarative higher-order-functions in TypeScript/JavaScript like map, reduce, forEach, filter and so on. There are also many libraries that provide functional interfaces to be used in TypeScript/JavaScript. Pure functions As we saw already a pure function should return values only based on the arguments passed and should not affect or depend on global state. It is possible to do this in TypeScript easily. This is quite simple, take the below this is a pure function. It will always return the same output for the given input and its behavior is highly predictable. We can safely cache the method if needed. function sum(a: number, b: number): number { return a + b; } If we add an extra line in this function, the behavior becomes unpredictable as it now has a side effect that affects an external state. const holder = {}; function sum(a: number, b: number): number { let c = a + b; holder[`${a}+${b}`] = c; return c; } So try to keep your functions pure and simple. Using tools like ESLint and typescript-eslint it is possible to enforce these. Recursion Functional programming favors recursion over looping. Let us see an example for calculating the factorial of a number. In traditional iterative approach: function factorial(num: number): number { let result = 1; for (; num > 0; num--) { result *= num; } return result; } console.log(factorial(20)); // 2432902008176640000 The same can be done using recursion as below which is favored in functional programming. const factorial = (num: number): number => num == 0 ? 1 : num * factorial3(num - 1); console.log(factorial(20)); // 2432902008176640000 The downside of the recursive approach is that it will be slower compared to an iterative approach most of the times(The advantage we are aiming for is code simplicity and readability) and might result in stack overflow errors since every function call needs to be saved as a frame to the stack. To avoid this tail recursion is preferred, especially when the recursion is done too many times. In tail recursion, the recursive call is the last thing executed by the function and hence the functions stack frame need not be saved by the compiler. Most compilers can optimize the tail recursion code the same way iterative code is optimized hence avoiding the performance penalty. Tail call optimization is part of the ECMAScript specs but unfortunately, most JavaScript engines do not support this yet. Now using tail recursion the same function can be written as below, but depending on the engine this might not be optimized, though there are workarounds, still it performed better in benchmarks. const factorialTailRec = (num: number): number => factorial(1, num); const factorial = (accumulator: number, val: number): number => val == 1 ? accumulator : factorial(accumulator * val, val - 1); console.log(factorialTailRec(20)); // 2432902008176640000 Consider using recursion when writing TypeScript code for readability and immutability, but if performance is critical or if the number of iterations will be huge use standard loops. Lazy evaluation Lazy evaluation or non-strict evaluation is the process of delaying evaluation of an expression until it is needed. In general, TypeScript does strict/eager evaluation but for operands like &&, || and ?: it does a lazy evaluation. We can utilize short-circuiting, higher-order-functions, closures, and memoization techniques to do lazy evaluations. Take this example where TypeScript eagerly evaluates everything. function add(x: number): number { console.log("executing add"); // this is printed since the functions are evaluated first return x + x; } function multiply(x: number): number { console.log("executing multiply"); // this is printed since the functions are evaluated first return x * x; } function addOrMultiply( add: boolean, onAdd: number, onMultiply: number ): number { return add ? onAdd : onMultiply; } console.log(addOrMultiply(true, add(4), multiply(4))); // 8 console.log(addOrMultiply(false, add(4), multiply(4))); // 16 This will produce the below output and we can see that both functions are executed always executing add executing multiply 8 executing add executing multiply 16 We can use higher-order-functions to rewrite this into a lazily evaluated version function add(x: number): number { console.log("executing add"); return x + x; } function multiply(x: number): number { console.log("executing multiply"); return x * x; } type fnType = (t: number) => number; // This is now a higher-order-function hence evaluation of the functions are delayed in if-else function addOrMultiply( add: boolean, onAdd: fnType, onMultiply: fnType, t: number ): number { return add ? onAdd(t) : onMultiply(t); } console.log(addOrMultiply(true, add, multiply, 4)); console.log(addOrMultiply(false, add, multiply, 4)); This outputs the below and we can see that only required functions were executed executing add 8 executing multiply 16 Or by memoization like this const cachedAdded = {}; function add(x: number): number { if (cachedAdded[x]) { return cachedAdded[x]; } console.log("executing add"); const out = x + x; cachedAdded[x] = out; return out; } const cachedMultiplied = {}; function multiply(x: number): number { if (cachedMultiplied[x]) { return cachedMultiplied[x]; } console.log("executing multiply"); const out = x * x; cachedMultiplied[x] = out; return out; } function addOrMultiply( add: boolean, onAdd: number, onMultiply: number ): number { return add ? onAdd : onMultiply; } console.log(addOrMultiply(true, add(4), multiply(4))); // 8 console.log(addOrMultiply(false, add(4), multiply(4))); // 16 This outputs the below and we can see that functions were executed only once for the same values executing add executing multiply 8 16 Please note that memoization techniques will work only when your functions are pure and referentially transparent. There are also other ways of doing Lazy evaluations like this. Doing Lazy evaluations in TypeScript might not be worth the code complexity some of the times, but if the functions in question are heavy in terms of processing then its is absolutely worth it to lazy evaluate them. Type system TypeScript has a strong type system and also has great type inference. While the underlying JavaScript itself is weakly typed, TypeScript along with a compatible IDE can bridge that gap. Referential transparency From Wikipedia: Functional programs do not have assignment statements, that is, the value of a variable in a functional program never changes once defined. This eliminates any chances of side effects because any variable can be replaced with its actual value at any point of execution. So, functional programs are referentially transparent. Unfortunately, there are not many ways to strictly limit data mutation in JavaScript, however by using pure functions and by explicitly avoiding data mutations and reassignment using other concepts we saw earlier this can be achieved. JavaScript by default passes primitive variables by value and objects by reference so we need to take care not to mutate data inside functions. Libraries like Immutable JS could also be considered. Use const as much as possible to avoid reassignments. For example, the below will produce an error const list = ["Apple", "Orange", "Banana", "Grape"]; list = ["Earth", "Saturn"]; But this will not help when variables are holding references to other objects, for example, the below mutation will work irrespective of the const keyword. const list = ["Apple", "Orange", "Banana", "Grape"]; list.push("Earth"); // will mutate the list list.push("Saturn"); // will mutate the list const keyword allows the internal state of referenced variables to be mutated and hence from a functional programming perspective const keyword is useful only for primitive constants and to catch reassignments. However, with TypeScript, we can use special mapped types to make objects read-only and hence avoiding accidental data mutations which are caught during compile time. Thanks to and for pointing it out. Read my post about mapped and conditional types here to learn more. const list: Readonly<string[]> = ["Apple", "Orange", "Banana", "Grape"]; list.push("Earth"); // will cause compilation error or const list: ReadonlyArray<string> = ["Apple", "Orange", "Banana", "Grape"]; list.push("Earth"); // will cause compilation error Other techniques to follow are using Object.freeze or built-in methods like map, reduce, filter and so on as they do not mutate the data. We can also use this ESlint plugin to restrict mutations. Data structures When using functional programming techniques it is encouraged to use data types such as Stacks, Maps and Queues which has functional implementations. Hence maps are better than arrays or hash sets in functional programming as data stores. Conclusion This is just an introduction for those who are trying to apply some functional programming techniques in TypeScript. There are a lot more that can be done in TypeScript and with the ever-evolving ECMAScript underneath, this should be even easier. As I said earlier functional programming is not a silver bullet but it offers a lot of useful techniques for more understandable, maintainable and testable code. It can co-exist perfectly well with imperative and object-oriented programming styles. In fact, we all should be using the best of everything. I hope you find this useful. If you have any question or if you think I missed something please add a comment. If you like this article, please leave a like or a comment. You can follow me on Twitter and LinkedIn. Source: Easy functional programming techniques in TypeScript for everyone – DEV Community 👩‍💻👨‍💻

    Read at 05:55 pm, Sep 14th

  • Italy's Russian Oil Scandal: The Russian Men At The Heart Of The Metropol Hotel Meeting

    Two men with deep ties to top figures in Russian politics are the voices caught on tape negotiating a proposed oil deal to fund Matteo Salvini’s far-right Lega party, according to new analysis revealed today. In July, BuzzFeed News uncovered an explosive audio recording of longtime Salvini aide Gianluca Savoini discussing a plan to covertly channel tens of millions of dollars of Russian oil money toward Lega’s upcoming European election campaign. The revelations have rocked Italian politics, and prosecutors in Milan are investigating the proposed deal. But the Russian men heard discussing the plan with Savoini and two other Italians at Moscow’s Metropol hotel last October have remained shrouded in mystery. Now a joint investigation by BuzzFeed News, the investigative journalism website Bellingcat, and Russian news site the Insider has identified two of the three Russian voices heard on the recording: Andrey Yuryevich Kharchenko and Ilya Andreevich Yakunin. They have links to the high-profile far-right demagogue Aleksandr Dugin and to Vladimir Pligin, a politician deeply enmeshed in President Vladimir Putin’s inner circle. Dugin is the father of an ideology that Putin has embraced in recent years, which sees a resurgent Russia standing as a bulwark against the liberal west. Pligin played a major role in one of Putin’s most hostile foreign policy moves, working on drafting a law in the country’s parliament certifying the 2014 annexation of Crimea. Neither Dugin nor Pligin attended the Oct. 18 Metropol meeting, but both were namechecked on the recording. One voice, as yet unidentified, declared that they needed Pligin’s “green light” before moving forward with the negotiations. And the day before the meeting, Dugin was photographed meeting with Savoini, while Salvini reportedly met with the Russian deputy prime minister Dmitry Kozak at Pligin’s office. The identities of two of the Russian attendees will pile renewed pressure on Salvini, Europe’s most powerful far-right leader, to explain whether he had any knowledge of the proposed deal or of the involvement of Savoini, a longtime aide who has been described as his "sherpa to Moscow." The new revelations also raise further questions about who was orchestrating the Russian side of the deal — and from how high up in Moscow. Reached by phone on Monday, Pligin told the Insider: “I have no relationship with these people.” Kharchenko, Yakunin, and Dugin did not respond to multiple requests for comment, or to detailed questions. Salvini did not attend the Metropol meeting, but he was in Moscow at the time. He has consistently refused to answer questions about whether he knew the meeting was taking place or was aware of the proposed oil deal. The identities of Yakunin and Kharchenko were established by matching their voices to other recordings. Yakunin’s voice can be heard on an interview he conducted with the Russian TV channel Arkhyz 24 in December 2017. Kharchenko’s voice was captured by The Insider during a telephone call last month. Analysis by Bellingcat comparing voice intonation, speech mannerisms, and sound frequency has determined with a high degree of confidence that Kharchenko’s and Yakunin’s voices match two of the men on the Metropol tape. The recordings of Yakunin’s voice have been sent to specialists at the National Center for Media Forensics at the University of Colorado Denver for a full forensic analysis. (The audio quality of Kharchenko’s voice on the Metropol tape is not high enough for such an in-depth analysis.) BuzzFeed News, Bellingcat, and the Insider also analyzed travel and company records, online and social media activity, and information contained in other databases, to piece together a profile of Yakunin and Kharchenko and their links to Dugin and Pligin. The Italian magazine L’Espresso first reported Yakunin’s name in February, but didn’t provide any further details. BuzzFeed News, Bellingcat, and the Insider can now confirm that it is Yakunin’s voice, which can be heard declaring on the recording that “we are waiting for Mr. Pligin to return” before discussing the proposed oil deal further. A longtime member of Putin’s United Russia party, Pligin is also a former senior member of parliament. His work on legislation to annex Crimea landed him on a European Union sanctions list. He co-founded a law firm with Deputy Prime Minister Kozak, a powerbroker — known as the “Cheshire Cat” because of his smile — who served as Putin’s chief of staff when he first became president, and who the US recently put on its sanctions list as a “member of the Russian leadership’s inner circle.” Pligin was also a student of Anatoly Sobchak, the former mayor of St. Petersburg known for kickstarting Putin’s political career. Pligin’s connection to Yakunin goes beyond the Metropol recording. In 2002, Yakunin took up a management position at the Agency of Direct Investments, a firm that focuses on major industries including oil and gas. The firm is controlled by a company that counts Pligin as one of its six founders and shareholders, records show. An employee at the agency told BuzzFeed News that Yakunin no longer works at the company. The ties to Dugin come from Kharchenko, who has been quoted in Russian media as an employee of Dugin’s far-right political group, International Eurasian Movement. But other details raise questions about what, exactly, Kharchenko does for a living. His name is nowhere to be found on the organisation’s website, and, according to two sources with access to the information, his tax records for the past five years are empty, showing no official income. Asked in a brief phone interview with the Insider last month why there was so little information about him online, Kharchenko said that he often published his writings using a pseudonym, but declined to say what it was. This week, BuzzFeed News sent Kharchenko multiple requests for comment on his role at the Metropol meeting. He did not respond. Kharchenko has traveled with Dugin on a number of foreign trips, including a November 2016 visit to Crimea to host a Turkish delegation, which included an adviser to President Recep Tayyip Erdogan. He also traveled with Dugin to Ankara that same month. On that trip, Kharchenko used a service passport, a document typically given to government or state employees. When Dugin met with Savoini the day before the Metropol meeting, an Italian journalist posted a photograph on Twitter showing them standing with another, unidentified man. There is a high probability that that man, standing with his back to the camera, is Kharchenko, according to a Bellingcat analysis comparing body measurements — such as the length of his arms relative to his height, as well as his posture and hair shape — to other photos of Kharchenko standing next to Dugin. However, because of the lack of specific features in the tweeted image, for example his ears aren't properly visible, it is not possible to provide a definitive forensic match. Kharchenko, who was born in Azerbaijan in March 1980 but became a Russian citizen 15 years later, is deeply sympathetic to Dugin’s ideology. Dugin even oversaw Kharchenko’s PhD in philosophy. Kharchenko’s 137-page dissertation focuses on the destructive qualities of globalization, cellphones, and selfies. Dugin has longstanding connections to the Russian government. He is the son of a KGB officer, and served as an adviser to Sergei Naryshkin, a former chairman of the State Duma who is now the director of Russia’s Foreign Intelligence Service. Dugin’s Facebook profile is filled with pictures of the far-right ideologue at events, nationalist posters and slogans, materials promoting his work, and flattering press photographs of nationalist politicians. It also features several images of Salvini and Savoini. In November 2016, Dugin interviewed Salvini about the election of Donald Trump and the future of Europe. “The EU, being an unnatural structure, has already started crumbling,” Salvini declared. “The European Union is a cell, the opposite of democracy.” Dugin’s relationship with Savoini, the Salvini aide at the centre of the Metropol scandal, appears to go back even further: Savoini has previously told BuzzFeed News that he has known Dugin for more than two decades. The Facebook page of Lombardy-Russia, the pro-Kremlin organisation led by Savoini, is packed with photos of the two men together. On the Metropol recording, Savoini can be heard telling the other Italians that “Aleksandr” had described Savoini as the “total connection” between the Italian and Russian sides — an apparent reference to Dugin. Savoini also met with Dugin in Rome on Sept. 25, just a few weeks before the meeting. His flight booking records suggest that he was booked on Aeroflot flight SU2404, from Moscow to Rome, the previous day. While much of Kharchenko’s background remains a mystery, a lot more can be pieced together about his counterpart Yakunin, who has ties to the influential politician Pligin. A LinkedIn profile claiming to belong to Yakunin suggests that he attended the Moscow State Institute of International Relations, which was once dubbed the “Harvard of Russia” by Henry Kissinger. The university is run by the Ministry of Foreign Affairs and is well-known for educating the country’s top diplomats, as well as its spies. Yakunin has spent much of his career working for firms with strong links to the Russian state. According to one news report, he has worked closely with some of Russia’s biggest companies, including Gazprom and Russian Railways. In 2016, he was appointed deputy director general of the North Caucasus Development Corporation, a firm created to attract investments and implement major infrastructure projects in the region. It was founded in 2010 by Vnesheconombank, a state-funded development bank under US Treasury sanctions and which has been accused of supporting the regime of Syrian President Bashar al-Assad. Two years ago the bank transferred control of the corporation directly to the Russian state. An employee at the North Caucasus Development Corporation told BuzzFeed News that Yakunin no longer worked there. More recently, Yakunin has referred to himself as the deputy director general of a new company called the Eurasian Trade and Logistics Centre. Records show that the company was created in February 2018. Its director Vladimir Georgievich Sobinsky, is a member of the United Russia party and an influential politician in Karelia, a region in the country’s northwest. One of the centre's shareholders is a company controlled by the Russian conglomerate Sistema, a multi-billion dollar investment company listed on the London Stock Exchange and run by the billionaire Vladimir Yevtushenkov. Reached by phone on Monday, a spokesperson at the centre told BuzzFeed News that Yakunin doesn’t work there, and hung up. Sistema did not respond to requests for comment. When Yakunin can be heard on the Metropol recording saying that “we are waiting for Mr. Pligin to return,” it is not clear where he is waiting for Pligin to return from. Pligin’s exact role in the affair, if any, is not yet known. Elsewhere on the recording, Russian voices can be heard referring to “yesterday’s meeting,” and discussing how they would have to feed details back to the “deputy prime minister” — details that would seem to line up with the alleged meeting between Salvini and Deputy Prime Minister Kozak held at Pligin’s office, which L’Espresso reported in February. Salvini has not denied the Kozak-Pligin meeting, saying instead he simply couldn’t recall. “I can’t remember what I did the day before yesterday,” he said in an Italian television interview. “It’s hard to remember what I did on October 17.” He added: “If the meeting did take place, it would be absolutely legitimate, and indeed proper.” Kozak has previously denied that he participated in the meeting. Italian prosecutors announced after the BuzzFeed News revelations that they were looking into whether Savoini and the two other Italians at the meeting had engaged in “international corruption.” In August, BuzzFeed News, Bellingcat, and the Insider revealed that Savoini travelled to Russia at least 14 times in 2018, and that a member of Salvini’s staff was booked on the same Aeroflot flights as Savoini from Milan to Moscow on Oct. 16 and on the evening of Oct. 18, following the meeting at the Metropol that morning. These revelations have come at the same time the Italian government has spun into crisis and collapsed, after Salvini pulled the plug on his coalition with the populist Five Star Movement. Prime Minister Giuseppe Conte said he would step down, and during his resignation speech lambasted Salvini over his refusal to address parliament about the Metropol revelations or share any information about it with Conte’s office. Salvini has tried to force a snap election, but that looks to have failed. The Five Star Movement is now on the brink of forming a new government with the centre-left Democratic Party, a move that would see Salvini lose his job as deputy prime minister and interior minister. Salvini has refused to answer an avalanche of questions from MPs and reporters about what he knows of the meeting. He and Savoini did not respond to requests for comment for this article. Aside from Savoini, two other Italian men attended the Metropol meeting. Both men — an international lawyer Gianluca Meranda and Francesco Vannucci, a consultant and banking expert — have come forward since the recording was exposed. They both deny wrongdoing and say a deal was never completed. Only one man from that meeting remains unidentified: A third Russian man, referred to as “Yuri.” Source: Italy’s Russian Oil Scandal: The Russian Men At The Heart Of The Metropol Hotel Meeting

    Read at 05:45 pm, Sep 14th

  • David Karpf Called Bret Stephens a Bedbug. Then the NYT Columnist Compared the Professor to a Nazi Propogandist.

    New York Times columnist Bret Stephens went searching for an online insult last Monday night. He found a Twitter joke I wrote, calling him a metaphorical bedbug as a riff on the actual bedbugs reported in the Times offices. He tried to put me in my place, emailing me from his Times account and copying my university provost, daring me to come to his house, meet his wife and children, and call him a bedbug to his face. It did not go well for him, and has resulted in a riveting public meltdown. The whole Internet, it seems, has had a nice laugh. The tweet originally garnered only nine likes and zero retweets; it now has over 32,000 likes and 4,800 retweets. There is something inherently entertaining about the self-proclaimed defender of uncomfortable speech on college campuses coming unglued when he found a sentence on the Internet that he didn’t like. Stephens may be the first person in history to publicly illustrate both the Streisand Effect and Godwin’s Law in a single episode. But the joke turned sour on Friday night, when he published a column that not-so-subtly compares me to a Nazi propagandist. There’s a lesson here, about power and introspection in modern America. But I have my doubts Stephens is ready to learn it. Stephens’s column on Friday, “World War II and the Ingredients of Slaughter,” has been roundly mocked. It is a rare moment, in 2019, when Breitbart, Fox News, the Washington Post, and Talking Points Memo can find something to agree upon. Stephens’s core argument is that the “rhetoric of infestation” was central to the Nazi propaganda campaign, and that today this same rhetoric is used by the American left to target the most vulnerable population of all—“the moderate conservative, the skeptical liberal, the centrist wobbler.” There is no greater injustice today, it seems, than Stephens’s hurt feelings. Stephens would do well to spend more time reading his own paper and less time trawling the depths of social media for perceived slights. He ought to read Michelle Goldberg on rising anti-Semitism and the revitalized Jewish Left. Twitter jokes from obscure academics are not where the armed violence targeting synagogues is coming from. He ought to read Sarah Jeong’s recent piece, “When the Internet Chases You From Your Home.” It takes an extraordinarily incurious mind to believe, in 2019, that the most vulnerable populations online are moderate Republicans like himself, given what women and people of color who dare to participate in public discourse routinely face. There is no greater injustice today, it seems, than Bret Stephens’s hurt feelings. The greatest irony is how easily this whole episode could have been avoided, or at least prematurely brought to a close. This should have been a goofy one-day story about barely anything at all. On Tuesday morning, Stephens could have simply said “I had a bad night. I shouldn’t have sent that email. I didn’t think the guy would post it to social media. That was embarrassing for me. I apologize, let’s move on.” That would have been the end of things. Barring that, he could have laid low for a week. He could have written a column about anything other than the “Bretbug” dustup. As a professor of strategic political communication, I could have told him that the only way for him to stop losing here is to stop playing. Instead, Stephens used the largest weapon at his disposal—his New York Times column—to imply that the Jewish professor who mildly teased him online was the equivalent of a Nazi propagandist. (Godwin’s Law, by the way, is meant to describe internet discussion forums, not published columns in the paper of record.) Stephens made no reference to anti-Semitic tropes in his original email to me. He had plenty of words for me—he declared that I lacked courage and intellectual integrity, and suggested that I was too much of a coward to say it to his face— but he did not come up with a supposed link between my joke and “totalitarian regimes” until the next morning, when Chris Jansing of MSNBC teased him about whether this was really the worst thing he had been called on social media. (He has agreed to come to George Washington University later this semester and I’ll have plenty to say to him in that setting.) As I have noted elsewhere, this was never about online civility. It was about power. Bret Stephens believed that, by virtue of his comfortable position at the New York Times, he ought to be immune from insult or criticism. Stephens tried to use his social position at the New York Times to punish me for joking about him. Instead of apologizing when that gambit blew up in his face, he invented an entirely new rationale to justify his overreaction. Despite everything that has happened, I am not canceling my Times subscription. The Times has too much good reporting, and too many excellent opinion columnists. Charlie Warzel’s privacy project is groundbreaking. I’m assigning the entire five-year Gamergate retrospective to my class this semester. The New York Times is supposed to cover the serious issues of the day. It’s supposed to enrich readers’ lives and challenge them with new information, ideas and perspectives. Most of the time, the paper succeeds. But there has to be some minimum threshold of quality for the paper’s columnists. If Stephens can abuse his position by searching out and threatening anyone who makes a joke about him online, and then devote an entire column to the nonsense personal vendetta that ensues, then I have to ask… how embarrassing is too embarrassing for Times editorial page editor James Bennet and his team? Source: David Karpf Called Bret Stephens a Bedbug. Then the NYT Columnist Compared the Professor to a Nazi Propogandist.

    Read at 04:33 pm, Sep 14th

  • Of Course Citizens Should Be Allowed to Kick Robots | WIRED

    Every day for 10 months, Knightscope K5 patrolled the parking garage across the street from the city hall in Hayward, California. An autonomous security robot, it rolled around by itself, taking video and reading license plates. Locals had complained the garage was dangerous, but K5 seemed to be doing a good job restoring safety. Until the night of August 3, when a stranger came up to K5, knocked it down, and kicked it repeatedly, inflicting serious damage. Robots engender human sympathy. Seen in the wild, they appear to have agency, feelings, and desires. R2D2’s spunk, C3PO’s intelligence, Wall-E’s charm. When delivery bots get stuck on the sidewalk, good Samaritans help them get unstuck. In light of the attack on K5, then, you may be thinking: Poor guy. The 5-foot-tall bot, whose shape is often described in phallic terms, indeed projects a certain charisma. Wobbling over the uneven pavement at 3 mph, it looks more like a bumbling neighbor than a robocop. When one K5 stationed in Washington, DC, rolled itself into a fountain in summer 2017, the internet worried it was suicidal. A writer at The Verge affirmed its choice: “I wouldn’t want your job either, K5. Live your truth.” Sure, sometimes people do get in the way. They’re curious. What’s this thing for, anyway? They’ll follow the robots to see what they do or tap their buttons to see what happens. “People want to explore them, and they don’t know how to do that,” says Bilge Mutlu, who runs the University of Wisconsin’s Human-Computer Interaction Lab. Rarely do the interventions cause damage. The incident on August 3, though, was not a case of poking around. Though the identity of the assailant remains unknown, video captured just before K5 crashed to the concrete shows a blurry image of a young person with dark hair running past the camera. This was likely premeditated. K5’s siblings, it turns out, don’t fare much better. In 2017 a drunk man attacked a K5 in a Mountain View parking lot. A few months later a group of angry protestors in San Francisco covered another one in a tarp, pushed it to the ground, and smeared barbecue sauce on it. Stacey Stephens, Knightscope’s executive vice president, wouldn’t say how many have been seriously damaged. “I don’t want to challenge people,” he says, afraid any number will inspire—perhaps compel—more miscreants to seek out K5s. (Stephens did specify that Knightscope prosecutes "to the fullest extent of the law," often pursuing felony charges for damaged K5s.) Hard numbers or not, the assaults will continue—that’s not the question. A brief history of humans and robots bears this out. Children bully them. Philadelphians behead them. In one incident, office workers bullied an HR chatbot so terribly that management wondered if the workers should be fired. Humans are mean to robots. The question is: Do we care? “We wouldn’t be having this conversation if people didn’t clearly view or treat robots differently than other devices,” says Kate Darling, a robot ethicist at MIT. “If people were going around smashing security cameras, you wouldn’t have called me.” Quite right. The vandalism of security cameras rarely piques my curiosity. But as an otherwise law-abiding citizen—I’m the kind of person who carefully refolds and reracks clothes after trying them on—all I could think as I watch and rewatch the security video from August 3 is: Way to go, dude. Because K5 is not a friendly robot, even if the cutesy blue lights are meant to telegraph that it is. It’s not there to comfort senior citizens or teach autistic children. It exists to collect data—data about people’s daily habits and routines. While Knightscope owns the robots and leases them to clients, the clients own the data K5 collects. They can store it as long as they want and analyze it however they want. K5 is an unregulated security camera on wheels, a 21st-century panopticon. Source: Of Course Citizens Should Be Allowed to Kick Robots | WIRED

    Read at 04:21 pm, Sep 14th

  • If You Really Want To Know Why Diversity Is Good… | Current Affairs

    Tucker Carlson attracted controversy recently when, on an episode of his show, he challenged the idea that “diversity” is a good thing. On his “Answer Me This” segment, he asked: How precisely is diversity our strength? Since you’ve made this our new national motto, please be specific as you explain it. Can you think, for example, of other institutions, such as, I don’t know, marriage or military units, in which the less people have in common the more cohesive they are? Do you get along better with your neighbors or your co-workers if you can’t understand each other or share no common values? Many people were disgusted by Carlson’s monologue, because it reiterated a set of notions commonly voiced by white nationalists: If you don’t see any value in having people who are different from you, and think cultural “cohesion” is crucial, you’ve laid the intellectual groundwork for ethnically-based immigration policies that explicitly prioritize people based on their similarity to, well, Tucker Carlson. (Indeed, a contributor to the neo-Nazi Daily Stormer has said that “Tucker Carlson is basically ‘Daily Stormer: The Show.’ Other than the language used, he is covering all of our talking points.”) But I want to assume Tucker Carlson isn’t actually a conscious white nationalist. I will assume that he’s a genuinely clueless person, who is asking in good faith, wearing that constant puzzled expression of his. Perhaps his question isn’t merely a rhetorical way of saying that diversity is purposeless, but a sincere effort to get the left to “answer me this.” Fortunately, the question is incredibly easily answered. Why is diversity a “strength”? Let me give an example. I live in New Orleans. It’s the only city I can ever see myself living in. It’s a unique place, and the factors that make it unique arise from the city’s multiculturalism, its mixture of African American, French, Spanish, Italian, Cuban, and Vietnamese influences. Its rich cultural diversity, its absorption of so many different kinds of people, has created a special and beloved place. Today’s Mardi Gras, for instance, draws from French Catholic sources, Old English sources, and African sources. The result is like nothing seen anywhere else in the world. It’s no accident that jazz, the most original and perhaps most important American contribution to world music, was developed in New Orleans. Here’s Jelly Roll Morton talking about the influence of Cuban “seasoning” that was necessary to perfect the jazz “recipe”: Then we had Spanish people there. I heard a lot of Spanish tunes. I tried to play them in correct tempo, but I personally didn’t believe they were perfected in the tempos. Now take the habanera “La Paloma”, which I transformed in New Orleans style. You leave the left hand just the same. The difference comes in the right hand — in the syncopation, which gives it an entirely different color that really changes the color from red to blue. Now in one of my earliest tunes, “New Orleans Blues”, you can notice the Spanish tinge. In fact, if you can’t manage to put tinges of Spanish in your tunes, you will never be able to get the right seasoning, I call it, for jazz. We have Mardi Gras Indians, an African American tradition inspired by Native Americans. We have Louis Prima, who mixed Italian folk with hot jazz, James Booker who combined classical European music with R&B, and Professor Longhair who threw rumba, mambo, and calypso into his singular musical stew. All of this was only possible because there were so many different types of people who lived here, because it was a multicultural city. And while it’s true that New Orleans has become more “cohesive” over time, with its own identity that transcends the differences, if people who came here had just shed their differences as soon as they arrived there wouldn’t have been the same opportunity to learn and borrow from different strong traditions. If they’d just assimilated into what was already there, nothing would ever have been added. (By the way, I am not suggesting that New Orleans was a kumbaya-singing ethnically harmonious paradise: It was a slave port, and many of its most beautiful cultural creations have arisen from tragedy and oppression. Nor is this over.) Here we have a genuine example of what multiculturalism can actually do: It can make life better and more interesting by combining the results of many different cultural experiments in spectacular ways. Diversity is good in part because it makes everything less bland. I have never understood people who complain about those who “refuse to speak English” but live in the United States. I think it’s cool to have people who don’t speak English! Other languages are fascinating and I’d hate to live in a place where there were no meaningful variations in how people spoke. Culturally speaking, diversity is an obvious strength: Mexican culture, which is amazing, is a hybrid of indigenous and Spanish, Tex-Mex is a hybrid of Mexican and American. If you want homogeneity and are uncomfortable around different types of people, then diversity is bad. If you are excited when you meet different types of people, then the more diversity the better. Perhaps Tucker Carlson looks at the graphic of all the little different people holding hands around the world and thinks it would be nice if they were identical. I do not. Perhaps he listens to Sly & the Family Stone’s “Everyday People” and retches in disgust. I do not. Every time I hear a white nationalist like Richard Spencer talking about his desire for a “white ethnostate,” I can’t help but think about just how awful such a place sounds. Okay, so multiculturalism is good for culture, because it gave us zydeco and Chicano art. But what about Carlson’s questions? First, are there other examples where people are more cohesive the less they have in common? And two, do you get along better with your coworkers if you “can’t understand each other or share no common values”? My colleague Briahna Gray pointed out, rather amusingly, that Carlson’s belief about marriages being improved by the similarity of partners provides a rather strong argument that all marriages should be same-sex marriages. More seriously, though, it may be true that the more similar you are to your coworkers in demographic characteristics, the easier it will be to avoid friction and misunderstanding. But I don’t think that’s a very good argument for not wanting people to be culturally different. The easiest world might be one in which everyone is as close to identical to me as possible, but that’s not a world I want to live in. Perhaps this is Tucker Carlson’s utopia…. Current Affairs puzzle: One of these Tuckers is not like the others. Can you tell which? (By the way, you may think this world is impossible. But Heaven knows FOX is trying...)  … but to me it’s an obvious nightmare. Yes, if one of my colleagues is from South Korea, it may be more difficult for us to find common reference points than if all of my colleagues are from my hometown of Sarasota. But it’s also exciting, because I know a lot about Sarasota already and I don’t know much about South Korea! I get to meet a person from a different place. How thrilling! (I am sorry that because this is so basic, I sound like I am explaining it to a child. But, well…)  I think “can’t understand each other or share no common values” is an insidious way of putting things, because it reinforces the idea that people can’t understand, say, Muslim immigrants and share no common values with them. That’s just false. “Won’t understand” and “can’t understand” are not the same thing, and often those who talk about the supposedly unbridgeable communication gap between different groups seem to spend little time listening to and trying to understand the people they’re talking about. Witness this travelogue in the Wall Street Journal by a man who went deep into the dark heart of Muslim Britain to find out what it was really like. He describes a terrifying world of “failed multiculturalism” in which “nobody made eye contact”: Within minutes, we walked by three other mosques, which were vibrant and filled with young men coming and going. We passed a church, which was closed and decrepit, with a window that had been vandalized with eggs. We squeezed by hundreds of residents busy preparing for the Eid al-Adha holiday. Girls in hijabs gathered around tables to paint henna designs on their hands. All the businesses had a religious flair: The eateries were halal, the fitness center was sex-segregated, and the boutiques displayed “modest” outfits on mannequins. Pakistani flags flew high and proud. I never saw a Union Jack. Painting henna designs on their hands! Eating the foods of their culture! Going to mosques instead of the church! Celebrating their holidays! Dressing modestly! What has happened to the England of yore? Ironically enough, the American writer attributes qualities to “Muslim” Britain that are actually just universal British qualities, like stand-offishness and “not flying the Union Jack.” He even wrote that in the Muslim neighborhoods, he saw signs that said “alcohol-restricted zone,” implying a creeping sharia regime. This necessitated a correction from the paper when it turned out that these are just a British thing: An earlier version mistakenly identified the public context of a sign that declared “Alcohol-restricted zone.” Such signs refer to a prohibition on public drinking and appear in many English neighborhoods, irrespective of Muslim population. More importantly than any of this, though, the writer doesn’t seem to have had a single substantive conversation with anyone in the neighborhood. (He did ask an imam for some leaflets.) This always happens with these guys: They suggest that cultural differences make it impossible to understand one another, without putting the slightest bit of effort into understanding other people’s perspectives. Interestingly, that’s actually one of the reasons that diversity takes on such importance. Because people like Carlson aren’t going to actually try to understand others, making sure there’s some integration is a way of helping them see that those different to themselves are human. White people who live only with other white people develop totally insane and incorrect understandings of what it’s like to be black, Mexican, or Muslim. They just have no idea, because they’ve never actually hung out in an ethnic neighborhood. The less contact people have, the less they really get to know each other, the more their differences can be a source of fear and suspicion. Ah, but isn’t that also an argument for conservatives’ favorite form of diversity, “viewpoint diversity”? What about the fact that colleges are dominated by liberals and leftists? Doesn’t that make them misunderstand conservatives? Well, yes, and I’ve always advocated that liberals consume conservative thought and try to understand how the right thinks. But because conservatism is ultimately a very obvious and accessible set of ideas, while differing life experiences are impossible to understand without empathetic encounters, I don’t see the intellectual loss of uniformity as being quite so substantial. I do think there’s a loss that comes from, say, not having any Mormon or Southern students, because it allows nasty stereotypes about Mormons and Southerners to proliferate unchecked. I think many elite colleges would benefit from having more veterans, older people, and working-class people. But I’m far more interested in that kind of diversity than, say, having all wealthy white people but making sure that some of them defend Trump’s border policies. And I also think there’s good reason why “marginalized” perspectives are given special attention: because they’re marginal, and without these perspectives we will be blind and ignorant. That’s actually a final benefit of diversity: If you’re not exposed to it, you’re going to be dumb. If you never speak to any Vietnamese people, you won’t understand the Vietnam War from Vietnamese perspectives, which means you won’t understand the Vietnam War. If you don’t talk to Muslims, then you’re not going to know why Islamophobic bigotry is so pernicious and painful. You need to be around different people, because the world is full of different people, and you don’t talk to them, your knowledge of the world will be cramped and biased. Part of me also wants to reject the idea that diversity even needs to make a case for itself. The benefits of diversity were often touted as a way to justify affirmative action programs, and there are major benefits. But generally my support for a multicultural society is not for the selfish reason that it creates a better society for me, even though it does. It’s because I don’t think it should matter whether people are different. We have an obligation to treat people fairly and try to get along with everyone, and that would be true even if multiculturalism did actually create problems in proportion to its many benefits. The fear here, of course, is that we don’t share enough “universal common understandings” to be sufficiently “cohesive.” I think that fear is generally overblown, because I tend to think that people are pretty similar to one another. Ironically, the Wall Street Journal’s “visit to Islamic Britain” confirms this: People were just being people. They were eating food, going to work, having fun, worshiping. They were entirely doing people-things. I know it’s a cliche, but when you get to know people, you often find that they’re not much different to yourself! (Oh God, if I got to know Tucker Carlson, would I discover the same thing?) We can achieve common understanding and social cohesion even across cultures because we’re all human and so we share so many problems and experiences in common. The main barrier to progress on this is people like Carlson who find the idea of being around different people disturbing and strange. The all-Carlson world I showed you above is horrifying, I’m sure you’ll agree. I don’t want to live in it, even though I somewhat look like him. Here, instead, is a vision of a better world, one with both rich difference and shared humanity: Note that Carlson is still allowed in the multiracial utopia! It isn’t about hating white men, it’s about asking some of them to stop being such intolerable asses. How can you say that diversity isn’t a strength? Our differences are what make us interesting. They distinguish us from ants and rocks. (Actually, rocks are very diverse.) We don’t have to all be the same in order to get along.  If you appreciate our work, please consider making a donation, purchasing a subscription, or supporting our podcast on Patreon. Current Affairs is not for profit and carries no outside advertising. We are an independent media institution funded entirely by subscribers and small donors, and we depend on you in order to continue to produce high-quality work. Source: If You Really Want To Know Why Diversity Is Good… | Current Affairs

    Read at 03:31 pm, Sep 14th

  • We Stand With The Kickstarter Union | Current Affairs

    A joint statement from project creators condemning Kickstarter’s anti-union retaliation… As Kickstarter project creators, we want to express our unequivocal support for Kickstarter’s workers in their attempt to form a union. We are disturbed that Kickstarter has fired multiple members of the union’s organizing committee and is pursuing aggressive tactics in an effort to keep its employees from exercising their basic right to bargain collectively.  The success of our Kickstarter projects is in part thanks to the hard work of the company’s staff, who have put together a powerful tool for helping small projects succeed. Some of us have worked with Kickstarter before, and would hope to work with Kickstarter again in the future. But it is impossible to partner with a company that does not respect its workers and is not willing to honor their rights. Kickstarter’s unlawful and unethical actions threaten to do significant damage to its platform, and to the creators who rely on it. Kickstarter’s unionizing workers have not asked for a boycott of the platform. However, if the union believed it undermined their organizing effort we could not continue to work with Kickstarter. We will support them throughout their campaign in any way they ask.  We call on Kickstarter to respect the unionization effort and and cease all illegal retaliation. We also ask other Kickstarter project creators to express their solidarity with Kickstarter workers’ union and to condemn the company’s firings. The Editorial Staff,  Current Affairs  The Editorial Staff,  Pinko Magazine The Editorial Staff Protean Magazine Additional signatories: Benjamin DeLoose, ESCAPE THE DARK: a horror feature Tonx Konecny, Yes Plz: Great Coffee Should Be for Everyone, “Kickstarter should be a leader in embracing a unionized workforce in tech.” Casey Donahue, I Am Root Beer  Eric Mersmann, My Jam  Nicholas O’Brien, The Last Survey, “I was a “Creator-in-Residence” at Kickstarter in the spring/summer of 2019. I sign in solidarity with Taylor, Clarissa, and the Kickstarter United efforts.” Elly Blue, Unf*ck Your Boundaries Kati Skelton, Pots N’ Tots David Cahn, “Umar” a children’s book for 2-4 year olds, “I mean come on kickstarter.” Keely Weiss, Riding in Subarus with Boys: A Killer Dark Comedy, “Let Kickstarter workers unionize!” Sara Edwards, Pakt Coffee Kit Molly Ostertag, Strong Female Protagonist: Books 1 and 2  Seth Boyer, The New Full-Length Studio Album from Seth Boyer, “Solidarity forever” Aaron Izzard, Pokemon Inspired Enamel Pin Set “Galar and Friends” (Canceled), “Dropped Kickstarter this morning to relaunch on Indiegogo as soon as I heard the news. A company like this won’t make a cent from any more of my projects.”  SamDakota, Color Me Gray rocket loam, memory/a film in 100 frames, “they’re gonna organize regardless, step out of the way” Pat Baer, Send 404ing It to PAX West 2017, “I fully support union efforts” If you have previously run or are currently running a Kickstarter campaign, please sign onto this statement here and your name and project will be added here. Source: We Stand With The Kickstarter Union | Current Affairs

    Read at 03:20 pm, Sep 14th

  • Remove Richard Stallman - Selam G. - Medium

    Why have I been blocked? This website is using a security service to protect itself from online attacks. The action you just performed triggered the security solution. There are several actions that could trigger this block including submitting a certain word or phrase, a SQL command or malformed data. Source: Remove Richard Stallman – Selam G. – Medium

    Read at 03:12 pm, Sep 14th

  • One in Four of New York’s New Luxury Apartments Is Unsold

    A quarter of the new condos built since 2013 in New York City have not yet found buyers, according to a new analysis of closed sales. Picture an empty apartment — there are thousands in Manhattan’s new towers — and fill it with the city’s chattiest real estate developers.

    Read at 12:41 am, Sep 14th

Day of Sep 13th, 2019

Day of Sep 12th, 2019

  • Why is the workplace a dictatorship?

    Eric Dirnbach reviews Elizabeth Anderson’s Private Government: How Employers Rule Our Lives (and Why We Don’t Talk about It). One of the primary critiques of capitalism from the Left for well over 150 years is that the capitalist workplace is a dictatorship.

    Read at 11:35 pm, Sep 12th

  • A Critical Examination of Caucus Organizing within the DSA — NYC Democratic Socialists

    Caucuses - organized factions that contest for power - are playing an increasingly prominent role in framing and setting the terms of political debate in DSA. Entering the main hall at the National Convention meant weaving through an obstacle course of caucus members handing out pamphlets.

    Read at 11:21 pm, Sep 12th

  • Caucusing is Clarifying — NYC Democratic Socialists

    “What is a caucus?” It’s a question that has been asked a lot in the discourse recently, especially after the latest National Convention. A caucus, defined in the simplest terms, is a conference of members around a particular set of issues.

    Read at 11:12 pm, Sep 12th

  • The Proud Boys Came to Portland and Are Threatening to Return. Now What?

    The protests that drew hundreds of far-right agitators, anti-fascist protesters, and armored police officers to Portland's waterfront Saturday, August 17, attracted far less violence and mayhem than predicted.

    Read at 11:09 pm, Sep 12th

  • Amber Rudd resigns as Boris Johnson's government plunged into further chaos

    Amber Rudd has sensationally quit the cabinet – and the Conservative Party – accusing Boris Johnson of misleading the country over wanting to avoid a crash-out Brexit.

    Read at 11:05 pm, Sep 12th

  • What Caused The Mass Panic At Newark Airport? Racism.

    When an Alaska Airlines employee yelled "evacuate" at a major New York–area airport on Labor Day, one of the busiest travel days of the year, it sent 200 panicked people fleeing amid fears of a mass shooting attack.

    Read at 10:59 pm, Sep 12th

  • He Helped Build an Artists’ Utopia. Now He Faces Trial for 36 Deaths There.

    Once a week, Max Harris is allowed to leave his 6-by-12-foot cell to go outside.

    Read at 10:51 pm, Sep 12th

  • AB5 Passes in California, Endangering Uber

    After the better part of year, California’s Senate today passed AB5, legislation that is expected to unravel the contractor business model of companies like Uber, Lyft, and Doordash. In a historic victory, workers for those companies and others like them are now likely to be considered employees, entitled to the benefits and protections that status conveys. Gig work platforms have faced mounting criticism that their businesses rely on the misclassification of their workforces as independent contractors, while depriving workers of any meaningful sense of self-determination—a key element of a contractor being truly independent. The absence of minimum workplace protections has left rideshare drivers paying out of pocket for work-related expenses like fuel and vehicle upkeep, while platforms determine how and when they shuttle passengers and can essentially fire them at any time for any reason. This lopsided arrangement has led to coordinated worldwide protests from this precarious workforce, who at least in California, are finally seeing some relief. The bill, which was introduced to the California State Assembly in January by Assemblywoman Lorena Gonzalez, still requires another pass in the Assembly and Governor Gavin Newsom’s sign-off. However, he recently signaled support for the initiative, and the Assembly is expected to easily pass the Senate’s version. “I am proud to be supporting Assembly Bill 5, which extends critical labor protections to more workers by curbing misclassification,” Newsom wrote in a Sacramento Bee op-ed published on Labor Day. “California has the power to act so these workers can have a real voice at work—one that can transform their lives and reshape our economy.” AB5 sailed through the House with a 53-11 vote, the Senate’s Labor, Public Employment, and Retirement Committee in a 3-1 vote, the Senate Appropriations Committee with 5-2 support. Today the bill passed the Senate’s full session 29-11, with all forty lawmakers voting along party lines after nearly two hours of deliberation. While Republican lawmakers crowed that the bill picked “winners and losers” the simple messaging of AB5's Senate floor jockey, Sen. Maria Durazo, captured the soul of the legislation: “One job should be enough.” AB5 effectively codifies into a law a state Supreme Court decision from mid-2018 which upheld that drivers working for a specific logistics company, Dynamex, were doing the work of employees rather than contractors as they had been classified. The Supreme Court reached this conclusion by applying a stringent test—called the ABC test—to determine if these workers had sufficient control over how they performed their work and found they did not. The three-part test, as relayed in the Supreme Court’s majority opinion, seeks to find if: (a) that the worker is free from the control and direction of the hirer in connection with the performance of the work, both under the contract for the performance of the work and in fact; and  (b) that the worker performs work that is outside the usual course of the hiring entity’s business; and  (c) that the worker is customarily engaged in an independently established trade, occupation, or business of the same nature as that involved in the work performed. The labor scheme those drivers were involved in very closely resembles the same independent contractors status gig workers are erroneously classified as. Driver groups like Gig Workers Rising, Rideshare Drivers United, and the Mobile Worker Alliance have vociferously supported AB5, holding frequent rallies, protests, and a multi-day caravan across the state. Uber, Lyft, and Doordash have reportedly engaged in a variety of tactics to build consensus against the bill, up to and including just throwing tens of millions of dollars at opposing it. Among their dirtier alleged tricks, drivers for both Uber and Lyft said they were requested to sign petitions opposing AB5, and the I’m Independent Coalition—a front-group funded in part by Instacart, Handy, Lyft, Postmates, Caviar, Uber, and DoorDash—was found to have paid drivers to show up and protest the bill. The extent to which AB5 would affect gig work platforms—particularly Uber and Lyft, which both have some of their biggest markets—as well as their corporate headquarters, in California, is unknown, though experts estimate it could add as much as 30 percent to labor costs. As top brass of Lyft and Uber, in a co-bylined editorial in the San Fransisco Chronicle, put it: “It’s also no secret that a change to the employment classification of ride-share drivers would pose a risk to our businesses.” Investors, perhaps fearful of AB5, have voiced their hesitation accordingly in the markets, where the stocks of both companies have been trending steadily downward. Earlier today Uber laid off over 400 employees from its product and engineering teams; about 400 marketing team staffers were also cut in late July as the company looked to trim costs. Leading up to AB5's passage, the Times reported, Uber and Lyft met quietly with Teamsters and Service Employees International Union leaders in an attempt to broker a less aggressive solution, though those talks eventually deteriorated. Publicly, the firms offered a $21/hour on-trip minimum wage—the crucial caveat being rideshare drivers can spend over half their travel time between trips, which would still be unpaid. While that particular option appears dead in the water with AB5's passing, gig work firms are likely to continue pushing against full employee status, pursuing additional methods to water down AB5, and they may find unlikely allies in other industries that will be impacted by the bill’s passing. Of course one of the key demands of rideshare drivers, and now one of their biggest hurdles, will be the process of joining or forming a union. “Unions and workers across the world have been watching CA. This will be a turning of the tides—the first in many victories in fighting global inequality exacerbated by techno-capital,” Veena Dubal, an associate law professor at the University of California, told Gizmodo. “The next step is legislation to support a strong, independent driver-led Union. Workers are going to win this.” “This victory is a direct result of drivers organizing. Drivers continued to fight for a voice in the face of insidious tactics by corporations to spread misinformation and squash their demands,” Gig Workers Rising wrote following the bill’s successful passage out of the Senate. “As we commemorate this moment we take renewed energy towards the fight for unionization.” “Today, our state’s political leadership missed an important opportunity to support the overwhelming majority of rideshare drivers who want a thoughtful solution that balances flexibility with an earnings standard and benefits,” Lyft wrote in a statement to Gizmodo. “The fact that there were more than 50 industries carved out of AB5 is very telling. We are fully prepared to take this issue to the voters of California to preserve the freedom and access drivers and riders want and need.” The version of AB5 passed by California’s Senate will return to the Assembly, where it is expected to pass before receiving Newsom’s signature. Once passed into law, the legislation will take effect January 1, 2020. About the author Bryan Menegus Senior reporter. Tech + labor /// bryan.menegus [at] gizmodo.com Keybase: keybase.io/bryangm Securedrop: http://gmg7jl25ony5g7ws.onion/ PGP Fingerprint: 1905 9104 D967 2EB7 C3F5 68F9 9108 1434 C917 C1B9 Source: AB5 Passes in California, Endangering Uber

    Read at 10:23 pm, Sep 12th

  • Is competition good? - LessWrong 2.0

    From Thiel's Zero To One: The problem with a competitive business goes beyond lack of profits. Imagine you’re running one of those restaurants in Mountain View. You’re not that different from dozens of your competitors, so you’ve got to fight hard to survive. If you offer affordable food with low margins, you can probably pay employees only minimum wage. And you’ll need to squeeze out every efficiency: that’s why small restaurants put Grandma to work at the register and make the kids wash dishes in the back. Restaurants aren’t much better even at the very highest rungs, where reviews and ratings like Michelin’s star system enforce a culture of intense competition that can drive chefs crazy. (French chef and winner of three Michelin stars Bernard Loiseau was quoted as saying, “If I lose a star, I will commit suicide.” Michelin maintained his rating, but Loiseau killed himself anyway in 2003 when a competing French dining guide downgraded his restaurant.) The competitive ecosystem pushes people toward ruthlessness or death. Scott Alexander replies: So monopolies’ advantages include being better for employees, more socially responsible, and able to engage in long-term thinking. The classic examples of this (which I don’t think Thiel brought up) are Bell Labs and Xerox PARC. Two monopolistic companies with more money than they knew what to do with started super-basic-blue-sky research centers that ended up creating many of the technologies that shaped the modern world On the other hand, all of the classical disadvantages of monopolies are still there. Monopolies remove the pressure to do a good job – whether that’s in keeping prices low, keeping working conditions tolerable, or in keeping products and service high-quality. They lower the diversity of an industry, making it more likely to get stuck in an evolutionary blind alley it can’t get out of; they increase the risk of merging with government into a crony capitalism. A wolf sheltered from survival-of-the-fittest for too long becomes a Chihuahua; Amazon sheltered from survival-of-the-fittest for too long becomes the DMV. And does a mythical take on it: I don’t think this is one of those issues that’s going to get decisively solved in a few paragraphs. Moloch and Slack are the new yin and yang, the new chaos and order; their interplay creates the Ten Thousand Things. Err too far towards competition and everyone works themselves to death in garment sweatshops; err too far towards monopoly and everyone sits at a desk filling out forms and backstabbing each other until the lights slowly go out. It’s only in the collision zone between the two that anything interesting ever happens. This post will attempt to decisively solve it in a few paragraphs. You have a restaurant. It's a local monopoly, and you're running decent profits. A new restaurant opens down the street. Some of your customers are diverted, so you lower your prices. You can no longer buy that sweet Ferrari. You have a restaurant. It's a local monopoly, and you're running decent profits. A new restaurant opens down the street. Some of your customers are diverted, so you lower your prices. You have to cancel most of your donations to AMF. In the first example, we can be reasonably sure that competition increased value. In the second example, we can be reasonable sure that competition decreased value. So here's a lemma: low competition is a good thing iff the increased profits are spent on something more valuable than distributing it among customers. Or let's put it this way: more resources to people that create above-average value is good. Let's call this type of person a "good" person. What makes a "good" person? Let's assume that "goodness" is largely dependent on incentives. There might be some residual factors like nature and habits, but these are generally small variations on the status quo that is dictated by the incentive landscape. Incentives can be instrumental and terminal, and the terminal ones are usually called "needs". Maslow was a pioneer in this field, but the most up-to-date list of needs that I can find is this 2011 paper. It says: There is indeed an ordering of needs, so that humans tend to take one at the time in an order that is roughly the same across people. The needs identified, in order of priority: Basic (being able to afford food and shelter) Safety (feeling safe walking alone, not having anything stolen, not being assaulted) Social (experiencing love, having others to count on in an emergency) Respect (feeling one is treated with respect, being proud of something) Mastery (having the experience of learning, doing what one does best at work) Autonomy (choosing how one's time is spent, experiencing freedom in life) The idea of hierarchical needs is that, as long as you don't have need i satisfied, you won't really care about needs {i+1, ..., n}. Someone who is struggling to gain respect, won't care as much about autonomy. Someone who is trying to feed themselves, won't care as much about safety. You have a restaurant. You're hardly getting by. A new restaurant opens down the street. Some of your customers are diverted. You need to survive, so you lower your cost price by secretly dumping your excess waste into the river. Conversely, even if your needs are satisfied up to i, you will not be able to allow things that threaten these needs: You have a restaurant. It's a local monopoly, and you're running decent profits. Many people in the neighborhood love and respect you for providing them with this service. You lobby with the local council to make sure no one else can open a restaurant near you. So an incorruptible person is one that has all of their needs met, but doesn't depend on anything for it. They can always make the moral choice, because no choices are incompatible with the foundation of their well-being. Which needs lead to altruism? Of the needs listed, I imagine that "respect" is the one that is most important for making people altruistic. It seems to me that respect is mostly a matter of fitting into the values that your local culture celebrates. Now this could be owning a Ferrari, or it could be donating to AMF. A culture can be seen as an agent, with its values being its operating system. A culture which values altruism will do better. This is one way in which altruism tends to make you better off: it will make you gravitate towards the people that value it too, surrounding you with altruistic people. This is a Darwinian process: altruistic cultures win, and cultures that win grow. Those that are invested in it grow along with it. But your investment only pays off when you are actually valued by the culture. This is, in my model, why people care about altruism at all. When is competition good? You have a restaurant. It provides you income. People love and respect you for it. Working in the kitchen gives you a sense of mastery. You're free to do it your way. A new restaurant opens down the street. Without your restaurant you are as good as dead, so you compete to the death. You dump your waste into the river, use the cheapest ingredients that are super toxic, put out annoyingly flashy ads for your restaurant. If all else fails, you are ready to have your opponents killed. You have a restaurant. It provides you income, but you could opt for a basic income programme instead. Your local community has a culture that values unconditional love. It also cares a lot about consequentialist utilitarianism, and by fitting in that belief system you get your respect. You get a sense of mastery out of your hobbies. A new restaurant opens down the street. You find that customers like it better. You put some effort into upgrading your service, but to no avail. You congratulate the owner and thank them for improving upon your work. You set off to find a new job that creates value. The kind of value that your community recognizes. Source: Is competition good? – LessWrong 2.0

    Read at 09:56 pm, Sep 12th

  • Deferrable SQL Constraints in Depth

    One strength of relational databases is their constant vigilance over data correctness. The user can declare constraints that their data must obey, and then leave it to the database to enforce the rules. This saves a lot of procedural application code and potential errors. Automatic constraint enforcement is a powerful feature, and should be leveraged whenever possible. However there are times when it is convenient – and even necessary – to temporarily defer enforcement. Table of Contents Constraint Checking Granularities As I mentioned in a previous article, transactions are the units of consistency, thus the SQL standard does not allow a transaction to commit with any constraint violations. While we can defer constraint checking to some degree, the buck stops at commit. To be more precise, PostgreSQL supports three enforcement granularities: By row. In this case a statement which updates multiple rows will fail immediately, possibly partway through its operation, when one of the rows violates a constraint. By statement. In this case a statement is allowed to make its full set of changes before the database checks for violations. By transaction. Any statement inside a transaction is free to violate constraints. However at commit time the constraints will be checked, and the transaction will fail if any constraints do not hold. By default PostgreSQL checks each constraint with granularity type one or two, depending on the constraint’s type. In the following table the NOT DEFERRABLE column lists the default for each constraint. NOT DEFERRABLE INITIALLY IMMEDIATE INITIALLY DEFERRED CHECK row row row NOT NULL row row row UNIQUE row statement transaction PRIMARY KEY row statement transaction FOREIGN KEY statement statement transaction EXCLUDE statement statement transaction To change a constraint’s validation granularity to the value in another column, we have to explicitly declare a constraint as deferrable. Notice that some types of constraints won’t budge. There’s no way to defer CHECK and NOT NULL any later than per-row. (This is PostgreSQL behavior which violates the SQL standard.) Before considering when/why to use deferrable constraints, let’s see how they work in general. The first step is marking a constraint deferrable like this: ALTER TABLE foo ADD CONSTRAINT foo_bar_fk FOREIGN KEY (bar_id) REFERENCES bar (id) DEFERRABLE INITIALLY IMMEDIATE; -- the magic line Deferrable constraints give transactions flexibility. Any transaction can choose to defer checking foo_bar_fk to the end: BEGIN; -- defer the constraint SET CONSTRAINTS foo_bar_fk DEFERRED; -- ... -- now we can do whatever we want to bar_id -- ... COMMIT; -- but it better be correct at this point Additionally, we can approach it the other way and mark the constraint DEFERRABLE INITIALLY DEFERRED. In this mode the constraint will be checked per-transaction by default. If a transaction wants to not defer it, the transaction must say SET CONSTRAINTS constraint_name IMMEDIATE. Without an explicit BEGIN command each statement runs in its own single-statement transaction where there is no difference between initially immediate or deferred. Attempting to defer constraints outside of a transaction will do nothing and warn, WARNING: 25P01: SET CONSTRAINTS can only be used in transaction blocks. There’s one other important caveat. As the table above shows, declaring UNIQUE or PRIMARY KEY constraint DEFERRABLE INITIALLY IMMEDIATE actually changes it from per-row to per-statement checking. Even if a transaction does not choose to defer those constraints, their granularity is still subtly altered. What’s the practical difference between per-row and per-statement checking? It’s best illustrated with an example. CREATE TABLE snowflakes ( i int UNIQUE ); INSERT INTO snowflakes VALUES (1), (2), (3); UPDATE snowflakes SET i = i + 1; The UNIQUE constraint here is not deferrable, so by default an UPDATE statement will enforce it per-row and will raise an error. ERROR: 23505: duplicate key value violates unique constraint "snowflakes_i_key" DETAIL: Key (i)=(2) already exists. If PostgreSQL had waited for all the rows to be updated (i.e. per-statement checking) thre would be no problem. The rows would be uniformly incremented and would end up unique again. However, as it is, PostgreSQL checks for violations immediately after incrementing i=1 to i=2, and at that moment the table contains 2, 2, 3. Checking constraints per row is fragile and depends on the physical ordering of the rows. If we had inserted items in the opposite order (INSERT INTO snowflakes VALUES (3), (2), (1)) then the update would have worked. To recap, declaring a constraint deferrable allows transactions to defer validation until commit time. Confusingly it also changes the semantics of some constraints even when no transaction chooses to defer them. Why Defer? Cyclic Foreign Keys Sometimes you have to. The classic example is creating two items related by cyclic consistency checks. Consider, CREATE TABLE husbands ( id int PRIMARY KEY, wife_id int NOT NULL ); CREATE TABLE wives ( id int PRIMARY KEY, husband_id int NOT NULL ); ALTER TABLE husbands ADD CONSTRAINT h_w_fk FOREIGN KEY (wife_id) REFERENCES Wives; ALTER TABLE wives ADD CONSTRAINT w_h_fk FOREIGN KEY (husband_id) REFERENCES husbands; Creating rows in either requires a row in the other to already exist. There’s no way to get started because foreign keys are checked per statement and inserting into two tables requires two statements. What we can do is to defer the constraint. ALTER TABLE husbands ALTER CONSTRAINT h_w_fk DEFERRABLE INITIALLY DEFERRED; ALTER TABLE wives ALTER CONSTRAINT w_h_fk DEFERRABLE INITIALLY DEFERRED; Because the cyclic constraints pretty much need to be deferred, we set this as their default behavior in a transaction. We can now run: BEGIN; INSERT INTO husbands (id, wife_id) values (1, 1); INSERT INTO wives (id, husband_id) values (1, 1); COMMIT; -- and they lived happily ever after What a neat and tidy textbook example. Only there’s a dirty little secret. PostgreSQL has an alternative to deferrable foreign keys for making this example work! Check this out: -- We won't need to defer them ALTER TABLE husbands ALTER CONSTRAINT h_w_fk NOT DEFERRABLE; ALTER TABLE wives ALTER CONSTRAINT w_h_fk NOT DEFERRABLE; -- Instead do the two inserts as a single statement WITH wife AS ( INSERT INTO wives (id, husband_id) VALUES (2, 2) ) INSERT INTO husbands (id, wife_id) VALUES (2, 2); Common table expressions (CTEs) allow multiple queries to be considered as the same statement when checking constraints. Since foreign keys are checked per statement this approach works fine. Although we found a workaround for deferring cyclic foreign keys, they’re still a good way to start thinking about deferrable constraints. Reallocating Items, One per Group In this example, we have a set of school classes each with exactly one assigned teacher. Suppose we would like to switch the teachers of two classes. The constraints make it hard to do. CREATE TABLE classes ( id int PRIMARY KEY, teacher_id int UNIQUE NOT NULL ); INSERT INTO classes VALUES (1, 1), (2, 2); The CTE trick will not work for swapping teachers because a non-deferrable uniqueness constraint is checked by row rather than by statement. -- fails unless deferrable since PostgreSQL -- checks per row, not per statement WITH swap AS ( UPDATE classes SET teacher_id = 2 WHERE id = 1 ) UPDATE classes SET teacher_id = 1 WHERE id = 2; ERROR: 23505: duplicate key value violates unique constraint "classes_teacher_id_key" DETAIL: Key (teacher_id)=(1) already exists. To swap teachers without deferring the uniqueness constraint on teacher_id, we would need to assign a temporary teacher to a class. -- first assign a temporary teacher to free -- teacher 1 to assignment to chass 2 UPDATE classes SET teacher_id = 999 WHERE id = 1; UPDATE classes SET teacher_id = 1 WHERE id = 2; UPDATE classes SET teacher_id = 2 WHERE id = 1; Using a temporary teacher is a bit of a hack. It’s more natural to create the table with a deferrable constraint instead. CREATE TABLE classes ( id int PRIMARY KEY, teacher_id int NOT NULL UNIQUE DEFERRABLE INITIALLY IMMEDIATE ); Then we can do a simple swap: BEGIN; SET CONSTRAINTS classes_teacher_id_key DEFERRED; UPDATE classes SET teacher_id = 1 WHERE id = 2; UPDATE classes SET teacher_id = 2 WHERE id = 1; COMMIT; Also a CTE approach with no explicit transaction will now work because a deferrable uniqueness constraint switches to per-statement checking. Renumbering a List One might model the position of todos todos in an ordered list using an integer position column: CREATE TABLE todos ( list_id int, position int, task text, PRIMARY KEY (list_id, position) ); INSERT INTO todos VALUES (1, 1, 'write grocery list'), (1, 2, 'go to store'), (1, 3, 'buy items'); Each todo’s position is unique per list because of the compound primary key constraint. Suppose we suddenly remember a new item for the top of the list, like “plan menus.” If we want the new item to have position 1 then we’ll need to shift the others down before inserting it. If you recall, this is the same basic problem as our earlier “snowflakes” example. Marking the primary key as deferrable would fix the problem: CREATE TABLE todos ( list_id int, position int, task text, PRIMARY KEY (list_id, position) DEFERRABLE INITIALLY IMMEDIATE ); This would have the effect of making PostgreSQL use per-statement semantics, with no actual SET ... DEFERRED needed in a transaction. UPDATE todos SET position = position + 1 WHERE list_id = 1; INSERT INTO todos VALUES (1, 1, 'plan menus'); However the best solution isn’t deferrable constraints, it’s better data modeling. Re-orderable position shouldn’t be stored as integers but as fractions (rational numbers). That way there is always room to find a number between any other two. See my article User-defined Order in SQL. Correct modeling in this case will elegantly remove the need for deferring constraints. Data Ingestion Deferring constraints while loading data can make the process more convenient. For instance, deferred foreign keys allow tables to be loaded in any order, children before parents. This can accommodate data arriving out of order, from the network perhaps. Some articles online claim that deferred constraints allow more efficient bulk inserts. By my measurement this appears to be a myth. At commit time the same number of elements must be checked for consistency, and the query time balances out. To reproduce: CREATE TABLE parent ( id int PRIMARY KEY, name text NOT NULL ); CREATE TABLE child ( id int PRIMARY KEY, parent_id int REFERENCES parent DEFERRABLE INITIALLY IMMEDIATE, name text ); INSERT INTO parent SELECT generate_series(1,1000000) AS id, md5(random()::text) as name; Now we insert five million child elements and measure the time to populate the data and check foreign keys. With a stock installation of PostgreSQL 9.6.3 on my wimpy laptop: INSERT INTO child SELECT generate_series(1,5000000) AS id, generate_series(1,1000000) AS parent_id, md5(random()::text) as name; -- -- Time: 89064.987 ms Trying again with the foreign key constraint deferred: BEGIN; SET CONSTRAINTS child_parent_id_fkey DEFERRED; INSERT INTO child SELECT generate_series(1,5000000) AS id, generate_series(1,1000000) AS parent_id, md5(random()::text) as name; -- -- Time: 40828.810 ms COMMIT; -- -- Time: 47211.533 ms -- Total: 88040.343 ms What time we save in the insert we lose again in the commit.The only way to make the ingest faster is to temporarily disable the foreign key (assuming we know ahead of time that the data to be loaded is correct). That’s generally a bad idea because we may end up taking more time to debug inconsistency down the line than it takes to do consistency checks during ingestion. Reasons Not to Defer Deferrable constraints seem great, right? Marking them deferrable simply gives us more options, so why not mark them all that way? Query Planner Performance Penalty The database query planner uses facts about the data to choose execution strategies that are correct and efficient. Its first duty is returning correct results, and it will use optimizations only when database constraints guarantee correctness. By definition deferrable constraints are not guaranteed to hold at all times, so they inhibit the planner. To learn more about this I asked Andrew Gierth, RhodiumToad, on IRC who explained, “The planner can determine that a set of conditions on a table forces uniqueness of the result. If there’s a compatible unique, non-deferrable index it can elide an otherwise necessary sort/unique or hashagg step. It can’t do that for deferrable constraints, because there might be actual duplicate values present.” He outlined two optimizations, one in PostgreSQL 9, and one in version 10. The older feature is join elimination: CREATE TABLE foo ( a integer UNIQUE, b integer UNIQUE DEFERRABLE ); EXPLAIN SELECT t1.* FROM foo t1 LEFT JOIN foo t2 ON (t1.a = t2.a); Notice the join disappears, and it’s a simple sequential scan: QUERY PLAN ---------------------------------------------------------- Seq Scan on foo t1 (cost=0.00..32.60 rows=2260 width=8) Whereas joining on b, which has a deferrable uniqueness constraint, does not eliminate the join. EXPLAIN SELECT t1.* FROM foo t1 LEFT JOIN foo t2 ON (t1.b = t2.b); Result: QUERY PLAN ---------------------------------------------------------------------- Hash Left Join (cost=60.85..124.53 rows=2260 width=8) Hash Cond: (t1.b = t2.b) -> Seq Scan on foo t1 (cost=0.00..32.60 rows=2260 width=8) -> Hash (cost=32.60..32.60 rows=2260 width=4) -> Seq Scan on foo t2 (cost=0.00..32.60 rows=2260 width=4) PostgreSQL 10 provides another optimization that turns the usual semi-join of an IN subquery into a regular join when the column of the subquery is guaranteed unique. EXPLAIN SELECT * FROM foo WHERE a IN ( SELECT a FROM foo ); It detects that a is unique: QUERY PLAN ------------------------------------------------------------------------- Hash Join (cost=60.85..121.97 rows=2260 width=8) Hash Cond: (foo.a = foo_1.a) -> Seq Scan on foo (cost=0.00..32.60 rows=2260 width=8) -> Hash (cost=32.60..32.60 rows=2260 width=4) -> Seq Scan on foo foo_1 (cost=0.00..32.60 rows=2260 width=4) Let’s try it on b. EXPLAIN SELECT * FROM foo WHERE b IN ( SELECT b FROM foo ); The deferrable constraint prevents the optimization: QUERY PLAN ------------------------------------------------------------------------- Hash Semi Join (cost=60.85..124.53 rows=2260 width=8) Hash Cond: (foo.b = foo_1.b) -> Seq Scan on foo (cost=0.00..32.60 rows=2260 width=8) -> Hash (cost=32.60..32.60 rows=2260 width=4) -> Seq Scan on foo foo_1 (cost=0.00..32.60 rows=2260 width=4) In addition to helping the planner, non-deferrable constraints are required for target columns of foreign keys. In order to point a foreign key at a column with a uniqueness or primary key constraint, that constraint must be non-deferrable or else the database throws an error. Harder Troubleshooting Getting errors only after a bunch of statements finish makes troubleshooting harder. The resulting error does not pinpoint which statement caused the problem. You may or may not be able to determine the statement based on the message. CREATE TABLE u ( i int UNIQUE DEFERRABLE INITIALLY IMMEDIATE ); BEGIN; SET CONSTRAINTS u_i_key DEFERRED; INSERT INTO u (i) VALUES (1), (2); -- ... other statments INSERT intu u (i) VALUES (2), (3); -- ... other statements INSERT intu u (i) VALUES (3), (4); COMMIT; At commit it says merely, ERROR: 23505: duplicate key value violates unique constraint "u_i_key" DETAIL: Key (i)=(2) already exists. In this case the message provides enough detail to trace it back to the offending statement, but it would be more difficult if the statements lacked literal values. Not only might errors at commit time be confusing for people, they can mess up object relational mappers (ORMs). The mappers are often designed for simplistic database access and may not all be programmed with enough knowledge of the SQL spec to properly handle transaction-level constraint failures. Also any work performed after a deferred constraint violation will ultimately be lost when the transaction rolls back. Deferred constraints can waste processing power. Misleading Self-Documentation Finally there’s the matter of expressing our intentions through the database schema. In a perfect world there would be no performance penalties for using deferrable constraints and all constraints would all be be deferrable (initially immediate). It’s an unfortunate implementation detail that we’re forced to choose manually between performance and flexibility. But given that the distinction exists, marking a constraint deferrable documents that deferring it is a worthwhile or necessary action. This is not the case for the vast majority of database constraints and labelling all as deferrable would hide the distinction. Deferring by Column Here’s one last trick for fun. The SET CONSTRAINTS command acts on constraints by their names. However it can be convenient to defer eligible constraints by column instead. PostgreSQL’s information schema allows us to look up constraints by column. -- A handy view CREATE VIEW deferrables AS SELECT table_schema, table_name, column_name, conname, contype FROM pg_constraint, information_schema.constraint_column_usage WHERE constraint_name = conname AND condeferrable = TRUE; -- Defer all constraints for column CREATE FUNCTION defer_col_constraints( t_name information_schema.sql_identifier, c_name name ) RETURNS void AS $ DECLARE names text; BEGIN names := ( SELECT array_to_string(array_agg(conname), ', ') FROM deferrables WHERE table_name = $1 AND column_name = $2 ); EXECUTE format( 'SET CONSTRAINTS %s DEFERRED', names ); END; $ LANGUAGE plpgsql; In our example loading parent and child data we could use this function to disable the child’s foreign key constraint like so: BEGIN; SELECT defer_col_constraints('child', 'parent_id'); -- ... COMMIT; Related Posts Source: Deferrable SQL Constraints in Depth

    Read at 03:33 pm, Sep 12th

  • MIT Built a Theranos for Plants

    The prestigious multidisciplinary MIT Media Lab built a “personal food computer” that worked so poorly that demos had to be faked Theranos-style, per a weekend report in Business Insider. Word of the project’s troubles comes as the Media Lab’s attempts to cover up its extensive financial ties to late financier and alleged sex trafficker Jeffrey Epstein have seriously damaged its credibility and led to resignation of its director, Joichi Ito. According to Business Insider, the project—a plastic hydroponic grow box filled with “advanced sensors and LED lights” that would supposedly make it possible to replicate crop conditions from any part of the global—was a sham, with MIT’s Open Agriculture Initiative director Caleb Harper resorting to faking demos: Ahead of big demonstrations of the devices with MIT Media Lab funders, staff were told to place plants grown elsewhere into the devices, the employees told Business Insider. In another instance, one employee was asked to purchase herbs at a nearby flower market, dust off the dirt in which they were grown, and place them in the boxes for a photoshoot, she said... The aim was to make it look like the devices lived up to Harper’s claims, the employees said. Those claims, which included assertions that the devices could grow foods like broccoli four times faster than traditional methods, landed Harper and his team articles in outlets ranging from the Wall Street Journal to Wired and National Geographic. All told, Business Insider’s sources said, the “personal food computers” amounted to hydroponic boxes that don’t work. Dietitian and former Open Agriculture Initiative project manager Paula Cerqueira told the site that the devices she worked on were “glorified grow boxes... with some sensors for collecting data,” and that on multiple occasions staff filled them with store-bought plants that had to be washed of dirt before presentations with funders. The boxes also weren’t air-tight, Cerqueira added, meaning that users couldn’t control for things like carbon dioxide levels or temperature and humidity. Cerqueira told Business Insider that out of dozens of units sent to schools in the Boston region, only a handful ever worked. On one occasion, the Media Lab sent 30 of them to schools, and “It’s fair to say that of the 30-ish food computers we sent out, at most two grew a plant,” Cerqueira told the site. On another, the Media Lab couldn’t make the boxes work in time for a demo with a representative from the Bezos Family Foundation, something Cerqueira told Business Insider was “super embarrassing.” In other words, this sounds a hell of a lot like Theranos, the disastrously failed startup that also promised a magical technology box (though in their case, it was fake blood-testing technology that helped the company achieve a valuation of $9 billion.) The food computer certainly doesn’t come anywhere the scale of Theranos, but Harper touted it with similarly grandiose claims: In a March 2019 video by Seeker, Harper stated that “You think Star Trek or Willy Wonka, that’s exactly what we’re going for.” According to Business Insider, Harper directed an email requesting comment to an MIT spokesperson, who “didn’t provide a comment.” [Business Insider] About the author Tom McKay "... An upperclassman who had been researching terrorist groups online." - Washington Post Source: MIT Built a Theranos for Plants

    Read at 03:03 pm, Sep 12th

  • Here Are 7 ‘Left Wing’ Ideas (Almost) All Americans Can Get Behind

    The Democrats’ 2020 primary fight has featured many pitched battles over policy questions. But every debate over Medicare for All, the Green New Deal, tuition-free college, or any other intraparty wedge issue has been colored by a meta-argument over the meaning of “political pragmatism.”

    Read at 01:58 pm, Sep 12th

  • West Village Lawyer Fighting 14th Street Busway Calls Advocates 'White Hooded Zealots'

    Arthur Schwartz doesn't take the bus—but he fancies himself a freedom rider nonetheless.

    Read at 01:40 pm, Sep 12th

  • How an Élite University Research Center Concealed Its Relationship with Jeffrey Epstein

    Update: On Saturday, less than a day after the publication of this story, Joi Ito, the director of the M.I.T. Media Lab, resigned from his position.

    Read at 01:37 pm, Sep 12th

  • Help (really, really) wanted

    Just this week, Amazon, Target and Wendy's have announced plans to hire a combined 180,000 new workers or temps, adding to the swelling number of jobs that so far outpaces the number of people who are available to work.

    Read at 01:34 pm, Sep 12th

  • America's worker deserts

    The U.S. unemployment rate is so low that some cities and states have turned into "worker deserts" — places where companies can't find people to hire.

    Read at 01:33 pm, Sep 12th

  • Elizabeth Warren leads Joe Biden in ranked-choice poll

    Former Vice President Joe Biden continues to lead the crowded Democratic field — but under a “ranked-choice” system designed to suss out the majority’s ultimate preference, Sen.

    Read at 01:30 pm, Sep 12th

  • Step-by-step guide for writing a custom babel transformation | Tan Li Hau

    Today, I will share a step-by-step guide for writing a custom babel transformation. You can use this technique to write your own automated code modifications, refactoring and code generation. What is babel? Babel is a JavaScript compiler that is mainly used to convert ECMAScript 2015+ code into backward compatible version of JavaScript in current and older browsers or environments. Babel uses a plugin system to do code transformation, so anyone can write their own transformation plugin for babel. Before you get started writing a transformation plugin for babel, you would need to know what is an Abstract Syntax Tree (AST). What is Abstract Syntax Tree (AST)? I am not sure I can explain this better than the amazing articles out there on the web: To summarize, AST is a tree representation of your code. In the case of JavaScript, the JavaScript AST follows the estree specification. AST represents your code, the structure and the meaning of your code. So it allows the compiler like babel to understand the code and make specific meaningful transformation to it. So now you know what is AST, let’s write a custom babel transformation to modify your code using AST. How to use babel to transform code The following is the general template of using babel to do code transformation: You would need to install @babel/core to run this. @babel/parser, @babel/traverse, @babel/generator are all dependencies of @babel/core, so installing @babel/core would suffice. So the general idea is to parse your code to AST, transform the AST, and then generate code from the transformed AST. code -> AST -> transformed AST -> transformed code However, we can use another API from babel to do all the above: Now, you have written your first babel tranform plugin that replace all variable named n to x, how cool is that?! Extract out the function myCustomPlugin to a new file and export it. Package and publish your file as a npm package and you can proudly say you have published a babel plugin! 🎉🎉 At this point, you must have thought: “Yes I’ve just written a babel plugin, but I have no idea how it works…”, so fret not, let’s dive in on how you can write the babel transformation plugin yourself! So, here is the step-by-step guide to do it: 1. Have in mind what you want to transform from and transform into In this example, I want to prank my colleague by creating a babel plugin that will: reverse all the variables’ and functions’ names split out string into individual characters into Well, we have to keep the console.log, so that even the code is hardly readable, it is still working fine. (I wouldn’t want to break the production code!) 2. Know what to target on the AST Head down to a babel AST explorer, click on different parts of the code and see where / how it is represented on the AST: Selecting the code on the left and see the corresponding part of the AST light up on the right If this is your first time seeing the AST, play around with it for a little while and get the sense of how is it look like, and get to know the names of the node on the AST with respect to your code. So, now we know that we need to target: Identifier for variable and function names StringLiteral for the string. 3. Know how the transformed AST looks like Head down to the babel AST explorer again, but this time around with the output code you want to generate. You can see that what used to be a `StringLiteral` is now a nested `BinaryExpression` Play around and think how you can transform from the previous AST to the current AST. For example, you can see that 'H' + 'e' + 'l' + 'l' + 'o' + ' ' + name is formed by nested BinaryExpression with StringLiteral. 4. Write code Now look at our code again: The transformation uses the visitor pattern. During the traversal phase, babel will do a depth-first search traversal and visit each node in the AST. You can specify a callback method in the visitor, such that while visiting the node, babel will call the callback method with the node it is currently visiting. In the visitor object, you can specify the name of the node you want to be callbacked: Run it and you will see that “string literal” and “identifier” is being called whenever babel encounters it: identifier identifier string literal identifier identifier identifier identifier string literal Before we continue, let’s look at the parameter of Identifer(path) {}. It says path instead of node, what is the difference between path and node? 🤷‍ In babel, path is an abstraction above node, it provides the link between nodes, ie the parent of the node, as well as information such as the scope, context, etc. Besides, the path provides method such as replaceWith, insertBefore, remove, etc that will update and reflect on the underlying AST node. You can read more detail about path in Jamie Kyle’s babel handbook So let’s continue writing our babel plugin. Transforming variable name As we can see from the AST explorer, the name of the Identifier is stored in the property called name, so what we will do is to reverse the name. Run it and you will see: We are almost there, except we’ve accidentally reversed console.log as well. How can we prevent that? Take a look at the AST again: console.log is part of the MemberExpression, with the object as "console" and property as "log". So let’s check that if our current Identifier is within this MemberExpression and we will not reverse the name: And yes, now you get it right! So, why do we have to check whether the Identifier’s parent is not a console.log MemberExpression? Why don’t we just compare whether the current Identifier.name === 'console' || Identifier.name === 'log'? You can do that, except that it will not reverse the variable name if it is named console or log: So, how do I know the method isMemberExpression and isIdentifier? Well, all the node types specified in the @babel/types have the isXxxx validator function counterpart, eg: anyTypeAnnotation function will have a isAnyTypeAnnotation validator. If you want to know the exhaustive list of the validator functions, you can head over to the actual source code. Transforming strings The next step is to generate a nested BinaryExpression out of StringLiteral. To create an AST node, you can use the utility function from @babel/types. @babel/types is also available via babel.types from @babel/core. So, we split the content of the StringLiteral, which is in path.node.value, make each character a StringLiteral, and combine them with BinaryExpression. Finally, we replace the StringLiteral with the newly created node. …And that’s it! Except, we ran into Stack Overflow 😅: RangeError: Maximum call stack size exceeded Why 🤷‍ ? Well, that’s because for each StringLiteral we created more StringLiteral, and in each of those StringLiteral, we are “creating” more StringLiteral. Although we will replace a StringLiteral with another StringLiteral, babel will treat it as a new node and will visit the newly created StringLiteral, thus the infinite recursive and stack overflow. So, how do we tell babel that once we replaced the StringLiteral with the newNode, babel can stop and don’t have to go down and visit the newly created node anymore? We can use path.skip() to skip traversing the children of the current path: …And yes it works now with now stack overflow! Summary So, here we have it, our first code transformation with babel: A summary of the steps on how we get here: Have in mind what you want to transform from and transform into Know what to target on the AST Know how the transformed AST looks like Write code Further resources If you are interested to learn more, babel’s Github repo is always the best place to find out more code examples of writing a babel transformation. Head down to https://github.com/babel/babel, and look for babel-plugin-transform-* or babel-plugin-proposal-* folders, they are all babel transformation plugin, where you can find code on how babel transform the nullish coalescing operator, optional chaining and many more. Reference Thank you for your time reading through this article. It means a lot to me. If you like what you have just read, Tweet about it so I will write more related articles; If you disagree or you have opinions about this article, Tweet about it too so I can take your suggestions and improve on it. Source: Step-by-step guide for writing a custom babel transformation | Tan Li Hau

    Read at 11:37 am, Sep 12th

Day of Sep 11th, 2019

  • The Amazon is burning, and your tiny human efforts against the climate crisis have never seemed so meagre

    Just when the burning of the Amazon forests drifted from our headlines, we learned that almost 4,000 new forest fires were started in Brazil in the two days after the government banned deliberate burning of the Amazon.

    Read at 11:44 pm, Sep 11th

  • Going for Thin Value

    The average poker player is too loose and too passive. I’m sorry, dear reader, but this probably includes you. The good news is that there are plenty of poker resources out there to tell you how to play more tightly.

    Read at 11:32 pm, Sep 11th

  • On Joi and MIT

    A couple of weeks ago, I signed a petition (the site has since been taken down, but you can see it at archive.org) expressing my support for Joi Ito. Not unexpectedly, that signing produced anger and outrage among many, and among some of my friends.

    Read at 04:55 pm, Sep 11th

  • Fox News' Tucker Carlson calls John Bolton 'a man of the left.' Bolton has worked in every GOP administration since Reagan.

    Fox News' Tucker Carlson isn't shedding any tears over the departure of former National Security Adviser John Bolton, who he now claims was a "man of the left" all along. Carlson, a longtime critic of Bolton, celebrated his ouster on his Tuesday show, calling it a "great day for America" and saying the "large number of young people who would have been killed in pointless wars if Bolton had stayed on the job" should be "celebrating," Mediaite reports. The Fox News host went on to contend that Bolton "fundamentally was a man of the left," even though he is a registered Republican who has served in every GOP presidential administration since Ronald Reagan's and campaigned for Barry Goldwater in 1964 at the age of 15, BBC News reports. Even so, Carlson labeled him "one of the most progressive" Trump administration officials and claimed "so many" progressives are "mourning" his departure. "There was not a human problem John Bolton wasn't totally convinced could be solved with the brute force of government," Carlson argued. "That's an assumption of the left, not the right. Don't let the mustache fool you." Although some on the left have spoken out about the circumstances surrounding Bolton's firing, The Washington Post's Dave Weigel observes that he has seen little actual praise for Bolton from progressives, with Weigel writing, "contempt for Bolton is hard-wired into even liberal DNA." Brendan Morrow Source: Fox News’ Tucker Carlson calls John Bolton ‘a man of the left.’ Bolton has worked in every GOP administration since Reagan.

    Read at 04:06 pm, Sep 11th

  • Logic Problems from my Youth

    One of the main reasons I find it easy to understand and explain the logic of poker is that my father gave me a logic problem almost every day from age seven to eleven.

    Read at 01:45 pm, Sep 11th

  • A Terrific Deal—For the Taliban

    The relentless spectacle of the Trump administration makes it difficult to hang onto even important blunders, but the recently revealed-only-to-be-canceled Camp David peace conference with the Taliban, as well as the underlying deal, is one the president ought to be forced to defend throughout the 2

    Read at 01:39 pm, Sep 11th

  • California Senate passes bill that could transform ride-sharing business for Uber and Lyft

    The California state senate passed legislation late Tuesday that could transition tens of thousands of ride-share drivers from independent contractors into full-time employees.

    Read at 01:35 pm, Sep 11th

  • by Brian Space

    Start with War. War, the card game where half the shuffled deck is dealt to two players. Next, each player exposes a card; the high card wins and collects both cards. Ties are broken by some iteration of the initial process.

    Read at 01:31 pm, Sep 11th

  • What does it Mean to be Good at Poker?

    What does it mean to be good at poker? Some would say it means being able to compete with the best in the world. This often involves playing in a balanced way to keep your opponents from taking advantage of you.

    Read at 11:42 am, Sep 11th

  • Netanyahu, Facing Tough Israel Election, Pledges to Annex a Third of West Bank

    His plan to annex territory along the Jordan River would reshape the Israeli-Palestinian conflict and would reduce any future Palestinian state to an enclave encircled by Israel. Mr.

    Read at 11:41 am, Sep 11th

  • Getting Started with Lottie: An Overview of Airbnb’s Awesome Animation Framework

    Recently I have been doing more animation work here at Delicious Brains Inc and finally decided to experiment with Airbnb’s rave-reviewed, Lottie framework. Lottie is a React Native, Android and iOS JavaScript library that makes it super easy to integrate animations into your website or app.

    Read at 11:23 am, Sep 11th

Day of Sep 10th, 2019

  • City Seeks Bids For Inwood Immigrant Art Center | Washington Heights, NY Patch

    INWOOD, NY — The city is currently accepting bids in a search for a nonprofit partner to establish a research and performance arts center in Inwood that focuses on the immigrant experience in New York City, city officials announced this month. The nonprofit with the winning big will be in charge of designing, constructing and operating the Immigrant Research and Performing Arts Center, city officials said. The city is allocating $15 million toward the construction of the center as part of the Inwood rezoning plan passed by the City Council in August 2018. "As the Inwood community continues to grow, we're thrilled to support local organizations and artists that reflect the rich culture that defines this neighborhood," New York City Economic Development Corporation CEO James Patchett said in a statement. The city EDC issued a request for bids for the center in early September and will accept submissions until Thursday, Dec. 19. The new center will provide space for local artists and cultural groups to stage and rehearse performances, city officials said. The center will also include classrooms, storage space and administrative offices. Programs at the center will be organized by the New York Public Library to educate the public on the immigrant experience in New York City through the research of historical documents and provide access to the library's many research collections. When the center is ultimately built, it will be included in the Department of Cultural Affairs' energy coalition, which means it will receive city funds to cover energy costs. This should allow the eventual operator of the center to dedicate more of its resources toward research and performing arts programs at the center, city officials said. "This center will be the first of its kind in the nation to research and celebrate the contributions of all immigrants. I am confident that this center will shed light onto the abundance of culture and language that makes New York City one the greatest cities in the nation," local City Councilmember Ydanis Rodriguez said in a statement. The city's investment in the center is part of a larger package of investments negotiated for Inwood when the City Council passed a controversial rezoning plan for the neighborhood in 2018. Rodriguez supported the rezoning plan largely due to the size of the investment package, but a group of local residents has since sued the city to reverse the rezoning. The group, called Inwood Legal Action, claims that the city's environmental review process for the "major upzoning" failed to analyze several effects of the rezoning plan on neighborhood residents including the displacement of longtime renters. Source: City Seeks Bids For Inwood Immigrant Art Center | Washington Heights, NY Patch

    Read at 10:03 pm, Sep 10th

  • Abusive parents: What do grown children owe the mothers and fathers who made their childhood a living hell?

    '):""},e.getDefinedParams=function(t,e){return e.filter(function(e){return t[e]}).reduce(function(e,n){return i(e,function(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}({},n,t[n]))},{})},e.isValidMediaTypes=function(t){var e=["banner","native","video"];return!!Object.keys(t).every(function(t){return Z()(e,t)})&&(!t.video||!t.video.context||Z()(["instream","outstream","adpod"],t.video.context))},e.getBidderRequest=function(t,e,n){return J()(t,function(t){return 0n[t]?-1:0}};var H=n(3),G=n(88),K=n.n(G),$=n(11),J=n.n($),Y=n(9),Z=n.n(Y),Q=n(10),X=n(89),tt=n.n(X);n.d(e,"deepAccess",function(){return tt.a});var et=n(90);n.d(e,"deepSetValue",function(){return et.a});var nt,rt=n(4),it="Array",ot="String",at="Function",st="Number",ut="Object",ct="Boolean",ft=Object.prototype.toString,dt=Boolean(window.console),lt=Boolean(dt&&window.console.log),pt=Boolean(dt&&window.console.info),ht=Boolean(dt&&window.console.warn),gt=Boolean(dt&&window.console.error),vt={checkCookieSupport:V,createTrackPixelIframeHtml:B,getWindowSelf:h,getWindowTop:p,getAncestorOrigins:l,getTopFrameReferrer:d,getWindowLocation:g,getTopWindowLocation:f,insertUserSyncIframe:R,insertElement:C,isFn:w,triggerPixel:D,logError:m,logWarn:y,logMessage:v,logInfo:b},bt={},yt=function(t,e){return e}.bind(null,1,bt)()===bt?Function.prototype.bind:function(t){var e=this,n=Array.prototype.slice.call(arguments,1);return function(){return e.apply(t,n.concat(Array.prototype.slice.call(arguments)))}},mt=(nt=0,function(){return++nt}),_t=function(){if(Array.prototype.indexOf)return Array.prototype.indexOf}(),Et=function(t,e){return t.hasOwnProperty?t.hasOwnProperty(e):void 0!==t[e]&&t.constructor.prototype[e]!==t[e]},St=z("timeToRespond",function(t,e){return eu;)r(s,n=e[u++])&&(~o(c,n)||c.push(n));return c}},143:function(t,e,n){var r=n(19).document;t.exports=r&&r.documentElement},144:function(t,e,n){var r=n(28),i=n(44),o=n(52)("IE_PROTO"),a=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=i(t),r(t,o)?t[o]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?a:null}},145:function(t,e,n){n(146);for(var r=n(19),i=n(21),o=n(30),a=n(14)("toStringTag"),s="CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,TextTrackList,TouchList".split(","),u=0;u=t.length?(this._t=void 0,i(1)):i(0,"keys"==e?n:"values"==e?t[n]:[n,t[n]])},"values"),o.Arguments=o.Array,r("keys"),r("values"),r("entries")},147:function(t,e,n){"use strict";var r=n(148),i=n(76);t.exports=n(150)("Set",function(t){return function(e){return t(this,0=y.syncsPerBidder?u.logWarn('Number of user syncs exceeded for "'.concat(e,'"')):p.canBidderRegisterSync(t,e)?(h[t].push([e,n]),void(v=function(t,e){return t[e]?t[e]+=1:t[e]=1,t}(v,e))):u.logWarn('Bidder "'.concat(e,'" not permitted to register their "').concat(t,'" userSync pixels.')):u.logWarn("Bidder is required for registering sync"):u.logWarn('User sync type "'.concat(t,'" not supported'))},p.syncUsers=function(){var t=0t.getTimeout()+v.b.getConfig("timeoutBuffer")&&t.executeCallback(!0)}function a(t,e){var n=t.getBidRequests(),r=_()(n,function(t){return t.bidderCode===e.bidderCode});!function(t,e){var n;if(t.bidderCode&&(0n&&(e=!1)),!e}),e&&t.run(),e}function c(t,e){void 0===t[e]?t[e]=1:t[e]++}var d=this;p=I,u=Date.now();var b=T.makeBidRequests(z,u,H,K,q);w.logInfo("Bids Requested for Auction with id: ".concat(H),b),b.forEach(function(t){!function(t){V=V.concat(t)}(t)});var y={};if(b.lengthe.max?t:e},{max:0}),a=s()(e.buckets,function(e){if(t>i.max*n){var o=e.precision;void 0===o&&(o=c),r=(e.max*n).toFixed(o)}else if(t=e.min*n)return e});return a&&(r=function(t,e,n){var r=void 0!==e.precision?e.precision:c,i=e.increment*n,o=e.min*n,a=Math.pow(10,r+2),s=(t*a-o*a)/(i*a),u=Math.floor(s)*i+o;return(u=Number(u.toFixed(10))).toFixed(r)}(t,a,n)),r}function o(t){if(u.isEmpty(t)||!t.buckets||!Array.isArray(t.buckets))return!1;var e=!0;return t.buckets.forEach(function(t){void 0!==t.min&&t.max&&t.increment||(e=!1)}),e}n.d(e,"a",function(){return r}),n.d(e,"b",function(){return o});var a=n(11),s=n.n(a),u=n(0),c=2,f={buckets:[{min:0,max:5,increment:.5}]},d={buckets:[{min:0,max:20,increment:.1}]},l={buckets:[{min:0,max:20,increment:.01}]},p={buckets:[{min:0,max:3,increment:.01},{min:3,max:8,increment:.05},{min:8,max:20,increment:.5}]},h={buckets:[{min:0,max:5,increment:.05},{min:5,max:10,increment:.1},{min:10,max:20,increment:.5}]}},42:function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},43:function(t,e,n){var r=n(24),i=n(56),o=n(44),a=n(34),s=n(83);t.exports=function(t,e){var n=1==t,u=2==t,c=3==t,f=4==t,d=6==t,l=5==t||d,p=e||s;return function(e,s,h){for(var g,v,b=o(e),y=i(b),m=r(s,h,3),_=a(y.length),E=0,S=n?p(e,_):u?p(e,0):void 0;E<_ in="" y="" if="" g="" e="" d="" r="n(33);t.exports=function(t){return" object="" n="Math.ceil,r=Math.floor;t.exports=function(t){return" isnan="" strict="" t="" i="" a="e==t.top,s={referrer:e.document.referrer||null,location:e.location.href||null,isTop:a};a&&(s=r(s,{canonicalUrl:n(e.document)})),i.push(s)}catch(a){i.push({referrer:null,location:null,isTop:e==t.top}),Object(o.logWarn)("Trying" to="" access="" cross="" domain="" iframe.="" continuing="" without="" referrer="" and="" location="" i.push="" t.location.ancestororigins="" e.href="" null="" function="" n.push="" o="n(0),a=i(window)},49:function(t,e,n){"use" s="" u="" this="" bid="" contains="" only="" vastxml="" will="" not="" work="" when="" prebid="" cache="" url="" is="" specified.="" try="" enabling="" with="" pbjs.setconfig="" cache:="" symbol="" void="" l="3<arguments.length&&void" p="" v="object" success="" error="" f="" window.xmlhttprequest="" xhr="" timeout="" after="" b="Object(a.c)(e,l);r(b.search,d),e=Object(a.a)(b)}p.open(h,e,!0),s.b.getConfig("disableAjaxTimeout")||(p.timeout=t),l.withCredentials&&(p.withCredentials=!0),u._each(l.customHeaders,function(t,e){p.setRequestHeader(e,t)}),l.preflight&&p.setRequestHeader("X-Requested-With","XMLHttpRequest"),p.setRequestHeader("Content-Type",l.contentType||"text/plain"),"function"==typeof" construction="" a.logerror="" load="" external="" script="" modulecode="" whitelisted="" for="" loading="" javascript="" attempting="" request="" empty="" executing="" callback="" m="" x="" h:return="" new="" _="" iterator="" i.call="" typeerror="" denis="" pushkarev="" t.reduce="" e.mediaquery="" iframe="" blocks="" sizeconfig="" from="" being="" correctly="" evaluated="" rule="" missing="" required="" property="" t.labelall="" h="Object(a.deepAccess)(d,"banner.sizes");p.shouldFilter&&h&&(d.banner.sizes=h.filter(function(t){return" p.sizessupported="" p.labels="">"):"";return'n n n prebid.org wrappern n ").concat(n,"n n n n ")}(t.vastUrl,t.vastImpUrl),ttlseconds:Number(t.ttl)};return"string"==typeof t.customCacheKey&&""!==t.customCacheKey&&(e.key=t.customCacheKey),e}e.b=function(t,e){var n={puts:t.map(r)};Object(i.a)(o.b.getConfig("cache.url"),function(t){return{success:function(e){var n;try{n=JSON.parse(e).responses}catch(e){return void t(e,[])}n?t(null,n):t(new Error("The cache server didn't respond with a responses property."),[])},error:function(e,n){t(new Error("Error storing video ad in the cache: ".concat(e,": ").concat(JSON.stringify(n))),[])}}}(e),JSON.stringify(n),{contentType:"text/plain",withCredentials:!0})},e.a=function(t){return"".concat(o.b.getConfig("cache.url"),"?uuid=").concat(t)};var i=n(5),o=n(3)},63:function(t,e,n){"use strict";function r(t){return(r="function"==typeof Symbol&&"symbol"==_typeof(Symbol.iterator)?function(t){return void 0===t?"undefined":_typeof(t)}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":void 0===t?"undefined":_typeof(t)})(t)}function i(){return(i=Object.assign||function(t){for(var e=1;e (eg mediaTypes.banner.sizes)."),t.sizes=n);if(e&&e.video){var i=e.video;if(i.playerSize)if(Array.isArray(i.playerSize)&&1===i.playerSize.length&&i.playerSize.every(function(t){return Object(f.isArrayOfNums)(t,2)}))t.sizes=i.playerSize;else if(Object(f.isArrayOfNums)(i.playerSize,2)){var o=[];o.push(i.playerSize),x.logInfo("Transforming video.playerSize from [".concat(i.playerSize,"] to [[").concat(o,"]] so it's in the proper format.")),t.sizes=i.playerSize=o}else x.logError("Detected incorrect configuration of mediaTypes.video.playerSize. Please specify only one set of dimensions in a format like: [[640, 480]]. Removing invalid mediaTypes.video.playerSize property from request."),delete t.mediaTypes.video.playerSize}if(e&&e.native){var a=e.native;a.image&&a.image.sizes&&!Array.isArray(a.image.sizes)&&(x.logError("Please use an array of sizes for native.image.sizes field. Removing invalid mediaTypes.native.image.sizes property from request."),delete t.mediaTypes.native.image.sizes),a.image&&a.image.aspect_ratios&&!Array.isArray(a.image.aspect_ratios)&&(x.logError("Please use an array of sizes for native.image.aspect_ratios field. Removing invalid mediaTypes.native.image.aspect_ratios property from request."),delete t.mediaTypes.native.image.aspect_ratios),a.icon&&a.icon.sizes&&!Array.isArray(a.icon.sizes)&&(x.logError("Please use an array of sizes for native.icon.sizes field. Removing invalid mediaTypes.native.icon.sizes property from request."),delete t.mediaTypes.native.icon.sizes)}}),t},"checkAdUnitSetup");T.getAdserverTargetingForAdUnitCodeStr=function(t){if(x.logInfo("Invoking pbjs.getAdserverTargetingForAdUnitCodeStr",arguments),t){var e=T.getAdserverTargetingForAdUnitCode(t);return x.transformAdServerTargetingObj(e)}x.logMessage("Need to call getAdserverTargetingForAdUnitCodeStr with adunitCode")},T.getAdserverTargetingForAdUnitCode=function(t){return T.getAdserverTargeting(t)[t]},T.getAdserverTargeting=function(t){return x.logInfo("Invoking pbjs.getAdserverTargeting",arguments),v.b.getAllTargeting(t)},T.getNoBids=function(){return x.logInfo("Invoking pbjs.getNoBids",arguments),a("getNoBids")},T.getBidResponses=function(){return x.logInfo("Invoking pbjs.getBidResponses",arguments),a("getBidsReceived")},T.getBidResponsesForAdUnitCode=function(t){return{bids:g.a.getBidsReceived().filter(function(e){return e.adUnitCode===t})}},T.setTargetingForGPTAsync=function(t,e){if(x.logInfo("Invoking pbjs.setTargetingForGPTAsync",arguments),Object(f.isGptPubadsDefined)()){var n=v.b.getAllTargeting(t);v.b.resetPresetTargeting(t),v.b.setTargetingForGPT(n,e),Object.keys(n).forEach(function(t){Object.keys(n[t]).forEach(function(e){"hb_adid"===e&&g.a.setStatusForBids(n[t][e],A.BID_STATUS.BID_TARGETING_SET)})}),O.emit(k,n)}else x.logError("window.googletag is not defined on the page")},T.setTargetingForAst=function(t){x.logInfo("Invoking pbjs.setTargetingForAn",arguments),v.b.isApntagDefined()?(v.b.setTargetingForAst(t),O.emit(k,v.b.getAllTargeting())):x.logError("window.apntag is not defined on the page")},T.renderAd=function(t,e){if(x.logInfo("Invoking pbjs.renderAd",arguments),x.logMessage("Calling renderAd with adId :"+e),t&&e)try{var n=g.a.findBidByAdId(e);if(n){n.status=A.BID_STATUS.RENDERED,n.ad=x.replaceAuctionPrice(n.ad,n.cpm),n.adUrl=x.replaceAuctionPrice(n.adUrl,n.cpm),g.a.addWinningBid(n),O.emit(R,n);var r=n.height,i=n.width,a=n.ad,u=n.mediaType,c=n.adUrl,f=n.renderer,d=document.createComment("Creative ".concat(n.creativeId," served by ").concat(n.bidder," Prebid.js Header Bidding"));if(x.insertElement(d,t,"body"),Object(S.c)(f))Object(S.b)(f,n);else if(t===document&&!x.inIframe()||"video"===u){var l="Error trying to write ad. Ad render call ad id ".concat(e," was prevented from writing to the main document.");s(N,l,n)}else if(a){if(navigator.userAgent&&-1Object(b.timestamp)()},C=function(t){return t&&(t.status&&!T()([x.BID_STATUS.BID_TARGETING_SET,x.BID_STATUS.RENDERED],t.status)||!t.status)},D=(h=_.a,v={},(g={}).setLatestAuctionForAdUnit=function(t,e){v[t]=e},g.resetPresetTargeting=function(t){if(Object(b.isGptPubadsDefined)()){var e=u(t),n=h.getAdUnits().filter(function(t){return T()(e,t.code)});window.googletag.pubads().getSlots().forEach(function(t){I.forEach(function(e){n.forEach(function(n){n.code!==t.getAdUnitPath()&&n.code!==t.getSlotElementId()||t.setTargeting(e,null)})})})}},g.resetPresetTargetingAST=function(t){u(t).forEach(function(t){var e=window.apntag.getTag(t);if(e&&e.keywords){var n=Object.keys(e.keywords),r={};n.forEach(function(t){T()(I,t.toLowerCase())||(r[t]=e.keywords[t])}),window.apntag.modifyTag(t,{keywords:r})}})},g.getAllTargeting=function(t){var e=1=e.length?{value:void 0,done:!0}:(t=r(e,n),this._i+=t.length,{value:t,done:!1})})},66:function(t,e,n){function r(){}var i=n(27),o=n(140),a=n(67),s=n(52)("IE_PROTO"),u="prototype",c=function(){var t,e=n(55)("iframe"),r=a.length;for(e.style.display="none",n(143).appendChild(e),e.src="javascript:",(t=e.contentWindow.document).open(),t.write(" ")}(r.script,r.impression_id);var c=o(x[r.size_id].split("x").map(function(t){return Number(t)}),2);u.width=c[0],u.height=c[1]}u.rubiconTargeting=(Array.isArray(r.targeting)?r.targeting:[]).reduce(function(t,e){return t[e.key]=e.values[0],t},{rpfl_elemid:s.adUnitCode}),e.push(u)}else y.logError("Rubicon bid adapter Error: bidRequest undefined at index position:".concat(i),n,t);return e},[]).sort(function(t,e){return(e.cpm||0)-(t.cpm||0)})},getUserSyncs:function(t,e,n){if(!O&&t.iframeEnabled){var r="";return n&&"string"==typeof n.consentString&&("boolean"==typeof n.gdprApplies?r+="?gdpr=".concat(Number(n.gdprApplies),"&gdpr_consent=").concat(n.consentString):r+="?gdpr_consent=".concat(n.consentString)),O=!0,{type:"iframe",url:T+r}}},transformBidParams:function(t,e){return y.convertTypes({accountId:"number",siteId:"number",zoneId:"number"},t)}},O=!1;Object(m.registerBidder)(I)}},[510]),pbjsChunk([60],{584:function(t,e,n){t.exports=n(585)},585:function(t,e,n){"use strict";function r(t){t.renderer.push(function(){window.ANOutstreamVideo.renderAd({targetId:t.adUnitCode,adResponse:t.adResponse})})}Object.defineProperty(e,"__esModule",{value:!0}),n.d(e,"spec",function(){return u});var i=n(0),o=n(1),a=n(12),s=n(2),u={code:"trustx",supportedMediaTypes:[s.b,s.d],isBidRequestValid:function(t){return!!t.params.uid},buildRequests:function(t,e){var n,r=[],o={},a={},s={},u="net";(t||[]).forEach(function(t){"gross"===t.params.priceType&&(u="gross"),n=t.bidderRequestId;var e=t.params.uid,c=t.adUnitCode;r.push(e);var f=i.parseSizesInput(t.sizes);a[e]||(a[e]={});var d=a[e];d[c]?d[c].bids.push(t):d[c]={adUnitCode:c,bids:[t],parents:[]};var l=d[c];f.forEach(function(t){s[t]=!0,o[e]||(o[e]={}),o[e][t]?o[e][t].push(l):o[e][t]=[l],l.parents.push({parent:o[e],key:t,uid:e})})});var c={pt:u,auids:r.join(","),sizes:i.getKeys(s).join(","),r:n,wrapperType:"Prebid_js",wrapperVersion:"2.26.0"};return e&&(e.refererInfo&&e.refererInfo.referer&&(c.u=e.refererInfo.referer),e.timeout&&(c.wtimeout=e.timeout),e.gdprConsent&&(e.gdprConsent.consentString&&(c.gdpr_consent=e.gdprConsent.consentString),c.gdpr_applies="boolean"==typeof e.gdprConsent.gdprApplies?Number(e.gdprConsent.gdprApplies):1)),{method:"GET",url:"//sofia.trustx.org/hb",data:i.parseQueryStringParameters(c).replace(/&$/,""),bidsMap:o}},interpretResponse:function(t,e,n){var o=2-1}});var instance=window.Layzr({threshold:100});instance.on("src:before",function(t){t.addEventListener("load",function(e){t.parentElement.classList.add("loaded")})}),document.addEventListener("DOMContentLoaded",function(t){instance.update().check().handlers(!0)}),DS.service("teadsBackfill",["$window",function(t){t.teadsNoFill=function(t){var e,n=window.innerWidth>969,r=window.innerWidthe?t:e}function a(){E.forEach(c)}function s(t,e){var n=e.visiblePx,r=e.visiblePercent;n&&r>=t.shownThreshold&&!t.seen?(t.seen=!0,setTimeout(function(){t.trigger("shown",new _("shown",e))},15)):(!n||r=0&&r.left>=0&&r.bottom1&&(a+=g(o,Math.floor(e/r),n-1,r)),a}function v(t,e){return i(e,o(t.bottom,0))-i(o(t.top,0),e)}function b(t){for(var e=t.offsetLeft,n=t.offsetTop;t=t.offsetParent;)e+=t.offsetLeft,n+=t.offsetTop;return{left:e,top:n}}function y(e,r){var i,o;return e=e.split(","),o=n.filter(n.map(e,function(e){return(i=t.querySelector(e))&&new m(i).on("shown",function(){n.invokeMap(o,"destroy"),r()})}))}var m,_,E=[];m=function(t,e){e=e||{},this.el=t,this.seen=!1,this.preload=!1,this.preloadThreshhold=e&&e.preloadThreshhold||0,this.shownThreshold=e&&e.shownThreshold||0,this.hiddenThreshold=e&&i(e.shownThreshold,e.hiddenThreshold)||0,E.push(this),c(this)},m.prototype={destroy:function(){E.splice(E.indexOf(this),1)}},r.enable(m.prototype),_=function(t,e){this.type=t,n.assign(this,e)},t.addEventListener("scroll",n.throttle(a,200)),this.getPageOffset=b,this.getLinearSpacialHash=g,this.getVerticallyVisiblePixels=v,this.getViewportHeight=f,this.getViewportWidth=d,this.isElementNotHidden=l,this.isElementInViewport=p,this.watchForAny=y,this.Visible=m}]);"use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};!function(){function e(t,n,o){function r(c,s){if(!n[c]){if(!t[c]){var a="function"==typeof require&&require;if(!s&&a)return a(c,!0);if(i)return i(c,!0);var u=new Error("Cannot find module '"+c+"'");throw u.code="MODULE_NOT_FOUND",u}var l=n[c]={exports:{}};t[c][0].call(l.exports,function(e){return r(t[c][1][e]||e)},l,l.exports,e,t,n,o)}return n[c].exports}for(var i="function"==typeof require&&require,c=0;c1){if(i=e({path:"/"},o.defaults,i),"number"==typeof i.expires){var s=new Date;s.setMilliseconds(s.getMilliseconds()+864e5*i.expires),i.expires=s}i.expires=i.expires?i.expires.toUTCString():"";try{c=JSON.stringify(r),/^[{[]/.test(c)&&(r=c)}catch(e){}r=n.write?n.write(r,t):encodeURIComponent(String(r)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),t=encodeURIComponent(String(t)),t=t.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),t=t.replace(/[()]/g,escape);var a="";for(var u in i)i[u]&&(a+="; "+u,!0!==i[u]&&(a+="="+i[u]));return document.cookie=t+"="+r+a}t||(c={});for(var l=document.cookie?document.cookie.split("; "):[],d=/(%[0-9A-Z]{2})+/g,f=0;f-1&&(console.log("removing serviceworker"),a.unregister())}}catch(e){r=!0,n=e}finally{try{!t&&o.return&&o.return()}finally{if(r)throw n}}});var t=e("../../services/client/slate-amplitude");window.addEventListener("load",function(){navigator.serviceWorker.register("/sw.js").then(function(e){},function(e){console.error("ServiceWorker registration failed: ",e)})}),window.addEventListener("beforeinstallprompt",function(e){e.userChoice.then(function(e){"dismissed"===e.outcome?t.track("PWA - dismissed install prompt"):t.track("PWA - Added to Home Screen")})})}}()},{"../../services/client/slate-amplitude":2}],2:[function(e,t,r){var n=void 0,i=function(e){return n||(e=e||document.querySelectorAll("script.js-analytics-js-data")[0],n=JSON.parse(e.textContent))},o=function(e,t,r){var n=i(),o={};Object.assign(o,n,t),amplitude.getInstance().logEvent(e,o,r)},a=function(e,t){amplitude.getInstance().logEvent("Loaded a Page",e,t)};t.exports.getDomEventData=i,t.exports.page=a,t.exports.track=o},{}]},{},[1]);"use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};!function(){function e(t,o,n){function r(s,c){if(!o[s]){if(!t[s]){var u="function"==typeof require&&require;if(!c&&u)return u(s,!0);if(i)return i(s,!0);var a=new Error("Cannot find module '"+s+"'");throw a.code="MODULE_NOT_FOUND",a}var f=o[s]={exports:{}};t[s][0].call(f.exports,function(e){return r(t[s][1][e]||e)},f,f.exports,e,t,o,n)}return o[s].exports}for(var i="function"==typeof require&&require,s=0;st&&o-t1){if(i=e({path:"/"},n.defaults,i),"number"==typeof i.expires){var c=new Date;c.setMilliseconds(c.getMilliseconds()+864e5*i.expires),i.expires=c}i.expires=i.expires?i.expires.toUTCString():"";try{s=JSON.stringify(r),/^[{[]/.test(s)&&(r=s)}catch(e){}r=o.write?o.write(r,t):encodeURIComponent(String(r)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),t=encodeURIComponent(String(t)),t=t.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),t=t.replace(/[()]/g,escape);var u="";for(var a in i)i[a]&&(u+="; "+a,!0!==i[a]&&(u+="="+i[a]));return document.cookie=t+"="+r+u}t||(s={});for(var f=document.cookie?document.cookie.split("; "):[],l=/(%[0-9A-Z]{2})+/g,p=0;p2592e6&&(n=0),["article","plus","coverstory"].includes(e)&&(n+=1),1===n&&(o=i),r.set(_,{pv:n,first:o},{expires:365}),n}function v(e,n){function r(e){var t=document.body.querySelector("."+e);if(t){var n=t.dataset.uri;if(n&&n.split("/")[2]===e)return t}}var a=window.amplitude.getInstance(),u=n||0,f=a.options&&a.options.deviceId;if(!f&&u0&&(m["Affiliate Link"]="✅"),r("product")&&(m["Product Component"]="✅");var b=r("newsletter-signup");m["Includes Newsletter Signup"]=o(b),b&&(m["Newsletter Signup Placement"]="In Content");var w=!g&&r("slate-roadblock");m["Includes Slate Plus Roadblock"]=o(w),x.page(m),t.removeFromLocation()}function m(e){var t=l();t&&(e.plan=t)}function g(e){var t=window.Scroll&&Scroll.config.detected;t&&(e.scrollUser=t)}function y(e){var t=window.Krux&&window.Krux.kuid;t&&(e.kuid=t)}function b(e){var t=r.get("AB");e.abTest=E(t)?t:"0"}function w(){var e=document.querySelector(".slate-paragraph--tombstone");if(e){var t=n.once(function(){x.track("Completed article"),document.removeEventListener("scroll",r)}),r=n.throttle(function(){var n="scrollY"in window?window.scrollY:window.pageYOffset;e.offsetTop+e.offsetHeight1){if(i=e({path:"/"},r.defaults,i),"number"==typeof i.expires){var a=new Date;a.setMilliseconds(a.getMilliseconds()+864e5*i.expires),i.expires=a}i.expires=i.expires?i.expires.toUTCString():"";try{c=JSON.stringify(o),/^[{[]/.test(c)&&(o=c)}catch(e){}o=n.write?n.write(o,t):encodeURIComponent(String(o)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),t=encodeURIComponent(String(t)),t=t.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),t=t.replace(/[()]/g,escape);var s="";for(var u in i)i[u]&&(s+="; "+u,!0!==i[u]&&(s+="="+i[u]));return document.cookie=t+"="+o+s}t||(c={});for(var l=document.cookie?document.cookie.split("; "):[],d=/(%[0-9A-Z]{2})+/g,f=0;f1){if(i=e({path:"/"},o.defaults,i),"number"==typeof i.expires){var s=new Date;s.setMilliseconds(s.getMilliseconds()+864e5*i.expires),i.expires=s}i.expires=i.expires?i.expires.toUTCString():"";try{c=JSON.stringify(r),/^[{[]/.test(c)&&(r=c)}catch(e){}r=t.write?t.write(r,n):encodeURIComponent(String(r)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),n=encodeURIComponent(String(n)),n=n.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),n=n.replace(/[()]/g,escape);var u="";for(var a in i)i[a]&&(u+="; "+a,!0!==i[a]&&(u+="="+i[a]));return document.cookie=n+"="+r+u}n||(c={});for(var d=document.cookie?document.cookie.split("; "):[],l=/(%[0-9A-Z]{2})+/g,f=0;f1){if(i=e({path:"/"},r.defaults,i),"number"==typeof i.expires){var a=new Date;a.setMilliseconds(a.getMilliseconds()+864e5*i.expires),i.expires=a}i.expires=i.expires?i.expires.toUTCString():"";try{c=JSON.stringify(o),/^[{[]/.test(c)&&(o=c)}catch(e){}o=n.write?n.write(o,t):encodeURIComponent(String(o)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),t=encodeURIComponent(String(t)),t=t.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),t=t.replace(/[()]/g,escape);var u="";for(var s in i)i[s]&&(u+="; "+s,!0!==i[s]&&(u+="="+i[s]));return document.cookie=t+"="+o+u}t||(c={});for(var f=document.cookie?document.cookie.split("; "):[],p=/(%[0-9A-Z]{2})+/g,d=0;d Source: Abusive parents: What do grown children owe the mothers and fathers who made their childhood a living hell?

    Read at 10:03 pm, Sep 10th

  • Trump Ousts John Bolton as National Security Adviser - The New York Times

    Video Secretary of State Mike Pompeo and Treasury Secretary Steven Mnuchin held a news conference in Washington after President Trump ousted John Bolton as national security adviser.CreditCreditErin Schaff/The New York Times WASHINGTON — President Trump announced on Tuesday that he had fired John R. Bolton, his third national security adviser, amid fundamental disagreements over how to handle major foreign policy challenges like Iran, North Korea and most recently Afghanistan. “I informed John Bolton last night that his services are no longer needed at the White House,” the president wrote on Twitter. “I disagreed strongly with many of his suggestions, as did others in the Administration, and therefore I asked John for his resignation, which was given to me this morning. I thank John very much for his service.” Mr. Bolton disputed the president’s version of how the end came in his own tweet shortly afterward. “I offered to resign last night and President Trump said, ‘Let’s talk about it tomorrow,’” Mr. Bolton wrote, without elaborating. Responding to a question from The New York Times via text, Mr. Bolton said his resignation was his own initiative, not the president’s. “Offered last night without his asking,” he wrote. “Slept on it and gave it to him this morning.” Mr. Trump said he would appoint a replacement “next week,” setting off a process that should offer clues to where the president wants to take his foreign policy before next year’s election. In the meantime, a White House spokesman said Charles M. Kupperman, the deputy national security adviser, would serve in an acting capacity. The national security adviser’s dismissal came so abruptly that it was announced barely an hour after the White House scheduled a briefing on terrorism for 1:30 p.m. at which Mr. Bolton was supposed to appear alongside Secretary of State Mike Pompeo and Treasury Secretary Steven Mnuchin. But Mr. Bolton left the White House, and the briefing proceeded without him. Mr. Pompeo, who has feuded with Mr. Bolton for months, shed no tears about the president’s decision. “He should have people he trusts and values,” Mr. Pompeo told reporters. And he made no effort to hide his rivalry with Mr. Bolton. “There were definitely places that Ambassador Bolton and I had different views about how we should proceed,” he said. Mr. Bolton’s departure comes as Mr. Trump is pursuing diplomatic openings with some of the United States’ most intractable enemies, efforts that have troubled hard-liners in the administration, like Mr. Bolton, who view North Korea and Iran as profoundly untrustworthy. He spent much of the past week waging a last-minute battle to prevent Mr. Trump from signing off on a peace agreement with the Taliban militant organization, which he viewed as anathema — a deal that the president was preparing to finalize by inviting the Taliban leaders to Camp David. Mr. Bolton urged Mr. Trump to reject the agreement, arguing that the president could still withdraw troops from Afghanistan to fulfill his campaign promise without getting in bed with an organization responsible for killing thousands of Americans over the past 18 years. Mr. Trump ultimately did scrap plans for the Camp David meeting and said on Monday that talks with the Taliban were now “dead.” But the president’s aides were furious over news reports saying that Mr. Bolton opposed the meeting because they saw the leaks as working against the president’s interests. Vice President Mike Pence’s camp likewise grew angry at news reports stating that he also opposed the Camp David invitation, interpreting them as an effort by Mr. Bolton’s allies to argue that he was not alone in his position. Both Mr. Trump and Mr. Pence publicly denied the reports, and some White House officials said they believed it was the last straw for the president. Mr. Bolton saw his job as stopping Mr. Trump from making unwise agreements with America’s enemies. “While John Bolton was national security adviser for the last 17 months, there have been no bad deals,” a person close to Mr. Bolton said minutes after the president’s announcement on Tuesday, reflecting the ousted adviser’s view. To Mr. Bolton’s aggravation, the president has continued to court Kim Jong-un, the repressive leader of North Korea, despite Mr. Kim’s refusal to surrender his nuclear program and despite repeated short-range missile tests by the North that have rattled its neighbors. In recent days, Mr. Trump has also expressed a willingness to meet with President Hassan Rouhani of Iran under the right circumstances, and even to extend short-term financing to Tehran. Mr. Pompeo confirmed on Tuesday that it was possible such a meeting could take place this month on the sidelines of the United Nations General Assembly session in New York. The rift between the president and his national security adviser owed as much to personality as to policy. Mr. Trump never warmed to him, a dynamic that is often fatal in this White House. Mr. Bolton’s critics inside the administration said he irritated the president by undermining policies even after they were decided. At its core, the schism reflected a deep-seated philosophical difference that has characterized the Trump presidency. While given to bellicose language, Mr. Trump came to office deeply skeptical of overseas military adventures and promising negotiations to resolve volatile conflicts. Mr. Bolton, however, has been one of Washington’s most outspoken hawks and unapologetic advocates of American power to defend the country’s interests. To his admirers, Mr. Bolton was supposed to be a check on what they feared would be naïve diplomacy, a cleareyed realist who would keep a president without prior experience in foreign affairs from giving away the store to wily adversaries. But Mr. Trump has long complained privately that Mr. Bolton was too willing to get the United States into another war. The tension between the men was aggravated in recent months by the president’s decisions to call off a planned airstrike on Iran in retaliation for the downing of an American surveillance drone and to meet with Mr. Kim at the Demilitarized Zone and cross over into North Korea. Mr. Bolton favored the strike on Iran and publicly criticized recent North Korean missile tests that Mr. Trump brushed off. After the president arranged the DMZ meeting with Mr. Kim via a last-minute tweet, Mr. Bolton did not accompany him and instead proceeded on a previously scheduled trip to Mongolia. Mr. Bolton’s departure caught White House aides and lawmakers off-guard. Senator Mitt Romney, Republican of Utah and a former party nominee for president, called the news “an extraordinary loss for our nation and the White House.” Mr. Romney said he was “very, very unhappy.” “John Bolton is a brilliant man with decades of experience in foreign policy,” he said. “His point of view was not always the same as everybody else in the room. That’s why you wanted him there. The fact that he was a contrarian from time to time was an asset, not a liability.” But Republicans like Senator Rand Paul of Kentucky who have tried to push Mr. Trump away from foreign intervention were openly gleeful. “The threat of war worldwide goes down exponentially with John Bolton out of the White House,” Mr. Paul told reporters. “I think his advocacy for regime change around the world is a naïve worldview, and I think that the world will be a much better place with new advisers to the president.” Among others pleased to be rid of Mr. Bolton were Iran’s leaders, who viewed him as an enemy of peace. Hesameddin Ashena, Mr. Rouhani’s top political adviser, tweeted that Mr. Bolton getting sidelined was “a definitive sign that Washington’s maximum pressure on Iran has failed” and that “Iran’s blockade will end.” A former under secretary of state and ambassador to the United Nations under President George W. Bush, Mr. Bolton, 70, was tapped as national security adviser in March 2018 after impressing Mr. Trump with his outspoken performances on Fox News. Mr. Bolton followed two military officers who held the post before him: Michael T. Flynn, a retired lieutenant general who stepped down after 24 days and later pleaded guilty to lying to the F.B.I.; and his successor, Lt. Gen. H. R. McMaster, who never forged a strong connection with the president and was forced out. Long before Mr. Trump popularized his “America First” slogan, Mr. Bolton termed himself an “Americanist” who prioritized a cold-eyed view of national interests and sovereignty over what they both saw as a fuzzy-headed fixation on democracy promotion and human rights. They shared a deep skepticism of globalism and multilateralism, a commonality that empowered Mr. Bolton to use his time in the White House to orchestrate the withdrawal of the United States from arms control treaties and other international agreements. With Mr. Trump’s backing, Mr. Bolton likewise helped enact policies meant to pressure the Communist government in Cuba, reversing some but not all of the measures taken by President Barack Obama in a diplomatic opening to the island. Among other things, the Trump administration imposed limits on travel and remittances to Cuba and opened the door to lawsuits by Americans whose property was seized in the revolution in 1959. But if Mr. Trump’s original national security team was seen as restraining a mercurial new commander in chief, the president found himself sometimes restraining Mr. Bolton. Behind the scenes, he joked about Mr. Bolton’s penchant for confrontation. “If it was up to John, we’d be in four wars now,” one senior official recalled the president saying. Mr. Trump also grew disenchanted with Mr. Bolton over the failed effort to push out President Nicolás Maduro of Venezuela. Rather than the easy victory he was led to anticipate, Mr. Trump has found himself bogged down in a conflict over which he has less influence than he had assumed. The political opposition backed by the White House could not turn Venezuela’s military against Mr. Maduro and has been stuck in a stalemate for months. The divergence between Mr. Trump and Mr. Bolton was on display in May during the president’s first trip this year to Japan. After Mr. Bolton told reporters then that “there is no doubt” that North Korean short-range missile launches violated United Nations resolutions, Mr. Trump dismissed the concern, still eager to preserve his strained relationship with Mr. Kim. “My people think it could have been a violation, as you know,” the president told reporters. “I view it differently.” Mr. Trump likewise repudiated the idea of working to overthrow the government of Iran, a goal Mr. Bolton long advanced as a private citizen. “We’re not looking for regime change,” Mr. Trump said. “I just want to make that clear.” Reporting was contributed by Maggie Haberman, Rick Gladstone and Farnaz Fassihi from New York, and Michael Crowley, Nicholas Fandos and Catie Edmondson from Washington. Follow Peter Baker on Twitter: @peterbakernyt. Source: Trump Ousts John Bolton as National Security Adviser – The New York Times

    Read at 08:50 pm, Sep 10th

  • What happened to Dave Chappelle: The cruelty of "Sticks & Stones" is a sign of the times | Salon.com

    On September 17, 2001, the great David Letterman became the first late night host to return to television days after 9/11 blindsided New York City, and the world. In his monologue, Letterman eschewed attempts at humor, choosing instead to meet the confusion and mourning blanketing America with an appropriate solemn acknowledgment. This became the pause the nation needed before feeling permission to laugh again in the wake of a horrific tragedy. Nearly 20 years later another great Dave — Dave Chappelle — has handed a different permission slip to Americans: one giving folks the green light to laugh at tragedies and, more to the point, the victims and survivors. Maybe that’s not being fair. The 9/11 attacks resulted in the deaths of 2,996 people and the injuries of more than 6,000 others — a massive culture-shifting calamity by any feeling person’s definition. In his new Netflix special “Sticks & Stones,” the objects of Chappelle’s ire are folks ostensibly making their personal tragedies everyone’s problems by using them to perpetrate career death and reputation homicide upon celebrities. These people, he tells us, have made him a “victim blamer.” He guns for two men at the center of “Leaving Neverland,” who allege that late pop music idol Michael Jackson molested them when they were 7 and 10 years old. And cancel culture advocates who took aim at his personal friends, like Kevin Hart and another A-list comic he was good friends with “before he died in that terrible masturbation accident.” Then there’s the easily offended “alphabet people,” his shorthand for the LGBTQ community, particularly, as he says, the “confusing” Ts. The #MeToo movement, organizers of school shooting drills — they’ve all gotten out of hand and need to be taken down a peg. Chappelle even takes a moment to stereotypically mimic Asians, which he defuses later on by reminding the audience that his wife is Asian. The entirety of “Sticks & Stones” is structured around such logic: by signaling to those who are true believers in his genius that it’s all just a joke, only words, this earns his ability to “punch down,” as the parlance goes. In that context, his stated position that he doesn’t believe Jackson’s accusers is about as true as his tall tale about “peppering” an opioid addict who had supposedly broken into his house with birdshot fired from his shotgun — which is to say, not true at all. And perhaps he doesn’t really mean it when he says, even if Jackson molested children, “I mean” — long pause — “it’s Michael Jackson! I know more than half the people in this room have been molested in their lives. But it wasn’t no goddamn Michael Jackson, was it? This kid had his d**k sucked by the King of Pop. All we get is awkward Thanksgivings for the rest of our lives!” Yes, perhaps it’s just words. But, to paraphrase a portion of one of his bits, dammit if his angle isn’t confusing. While several critics and comedians liken the latest form Chappelle’s taken to a grumpy, middle-aged uncle who refuses to evolve with the times, I have a different theory. On the whole, “Sticks & Stones” exists as a defiant design to intentionally offend large swaths of the audience Chappelle deems too thin-skinned and easily outraged, too quick to find offense, while serving up simple, low-bar yucks to anyone yearning for validation of their anti-P.C. stance. Indeed, it may end up being one of the defining comedy specials of our time. Because our time is defined by cruelty. Me-first, to-hell-with-everyone-else cruelty. The shock being felt by Chappelle’s fans who fell in love with “Killin' Them Softly” Dave, (circa 2000), or the Dave who walked away from “Chappelle’s Show” and a $50 million deal with Comedy Central (vintage 2005) are the ones who still see Chappelle as a man concerned about being socially irresponsible, and “pay[ing] attention to things like your ethics,” as 2006 Dave tells Oprah Winfrey during their famous conversation on her show. Apparently there’s a huge difference between a cable network $50 million deal in 2005 and a Netflix deal for $60 million in the present. These are the same folks who may have been incensed by Chappelle’s soft offer to give Donald Trump a chance during his post-election monologue on “Saturday Night Live.” What they should have been paying attention to was the part where he said this: All my black friends who have money said the same thing when Trump got elected. They said, ‘That’s it, bro. I’m out. I’m leaving the country. You coming with us?’ ‘Nah, I’m good, dog. I’m gonna stay here and get this tax break, see how it works out.’ Because that’s how it is being Dave Chappelle! These are the people caught off guard at the transphobic jokes in his 2017 set worked out during his Radio City Music Hall residency (which also turned up in his first two Netflix specials), only to be soothed by his 2018 interview with Van Jones. “We’re living in a time where there’s got to be more cultural sensitivity,” Chappelle, circa October 2018, tells Jones, later adding, “Even a guy like me that’s just writing jokes, I have to listen more than I’ve ever had to listen, because the gripes is coming so fast and furious, and I’m not dismissive of people’s gripes. Might sound like it on stage, but I listen.” But October 2018 is a lifetime ago — long enough for Chappelle’s views on transgender people to evolve to a place of befuddlement, but not long enough for Chappelle to temper his anger at cancel culture turning his world into “celebrity hunting season.” * * * Throughout his career, Chappelle constantly reminds us of the complicated territory comedians travel in relationship with their fans, without whom they wouldn’t be working. And yet, the most successful comedians tend to be the ones who resist the roles and labels fans assign to them, who test the limits of their craft and fan loyalty along with it. We want our comedians to be some version of truth-tellers. Even if we don’t hold them them to a standard of being entirely factual, we at least expect something genuine from them. We also want them to remind us of our shared humanity, even in cases where the socio-economic divide between them and us is vast. ”I think words aren’t what hurts people as much as the intentions behind words,” the 2006 version of Chappelle observes in an episode of “Iconoclasts.” “I think that’s been one of the reasons that I can say some pretty ugly words that would traditionally hurt people, but when they hear it, they feel like, this isn’t hurting me. Because they can feel the intention behind the words. Just in how I’m using it.” That episode is an interesting one to watch in the context of the current furor over “Sticks & Stones,” since it features Chappelle in conversation with Maya Angelou specifically about words. To the late Angelou, words hold tremendous power. The boyfriend of Angelou’s mother raped her when she was eight years old.  He was found guilty and was jailed for only one day, but days after his release, he was kicked to death. This event led her to stop speaking for years, because she believed her words killed a man. But we are speaking of today’s nonchalant cruelty: October 2018 also happens to be the month that The Atlantic’s Adam Serwer wrote about the delight Donald Trump’s base takes in watching the people it loathes suffer under his policies. “The cruelty is the point” is that story’s headline, and it became a recurring refrain on social media, accompanying the sharing of stories representing new lows in morality. If art is shaped by the times in which an artist lives, then surely we cannot be surprised when some artists capitalize on that moment’s reigning emotions. Our times also are defined by a lack of historic context and forgetting, which may explain why a comic so reverent of a woman who was also raped as a child during their 2006 meeting might find it perfectly OK to play his disbelief of Jackson’s accusers for rowdy giggles. Cruelty is a frequently used spice in all kinds of comedy, of course; even a guy like Jim Gaffigan, who works as squeaky clean as you can imagine, is viciously cruel, but mostly to himself. In 2019, when cruelty is the headliner, the country’s most successful stadium comic is killing it at every stop with unfettered screeds about immigrants, slapping political rivals with nicknames like Pocahontas. Cruelty is in fashion, so can we be surprised when an artist widely understood to be a genius in his form seizes our attention by Jackson Pollock-ing up the place with a set filled with jokes that would’ve walked other rooms had they been delivered by so-called lesser comedians? * * * Cruel humor is facile and blunt, as easy to tap as pushing a lame old man into a mud puddle and posting the video on WorldStar. Wielding it at the expense of the disenfranchised is beneath the best of us. But maybe not the richest of us.  And so much of today’s cruelty is born of an economic divide that gives the better-off license to “go there” in a culture that rewards oh no you didn’t! brashness. “Few people may be aware that a taboo exists, but everyone knows when it’s violated. A stand-up comedian appears on stage and, with no preface, simply yells an obscenity; people are likely to laugh and applaud as if he has said something funny,” wrote Joyce Carol Oates in 1990. “… Because taboos are part of what we are taught as children, their violation — and the rowdy applause it engenders — is a defiant cry to parents, elders, and custodians of authority: You don’t control us after all.” Comedy’s rules, such as they are, are structural, not tonal. There are people who would say that actually, no, comedy has plenty of rules. They'd list the classics: never punch down, don’t prey on the vulnerable, and don’t joke about hurting children. But that’s not entirely accurate. Successful comedy can punch every which way and land its humor, as long as the context works. No subject matter is truly off-limits if — and this is important — the joke is constructed correctly. The late George Carlin bellowed out entire acts revolving around this idea. But the question is whether the shocking statement a comedian makes is ludicrous enough to signal to the audience that he is actually kidding. What’s catching people off guard in this scenario is that it’s not quite clear that Chappelle is joking about, say, not believing Jackson’s accusers, any more than he appeared to be joking when he pantomimed Bill Clinton getting fellated by Monica Lewinsky in 2000, then launching her into the world with, “Now, go on out there and be somebody!” In the year 2000, that line was hilarious. But in that long-ago time, we were also still recovering from Andrew Dice Clay’s lazy, sexist, racist limericks that dominated comedy in the late ‘80s. Oates’ quote refers to him and observes that when Clay referred matter-of-factly to women as ”chicks,” ”whores,” and ”sluts” who ”are always looking for someone to treat them like the pigs they are,” that the people in his audience who laughed and applauded most enthusiastically are women.  And Clay’s ascent parallels the rise of Reagan-era, “greed is good” conservatism, right-wing talk radio and professional insult cannons like Rush Limbaugh. Clay’s career teaches us an important lesson about comedy that’s cruel for its own sake: It has a limited shelf life. He rose to fame as a shock comic, built to devour political correctness before such a concept was labeled, and a number of comedians followed that pattern, men and women alike. But shock wears off and eventually the audience tires of it. The audience at large turned on Andrew Dice Clay, and he failed to transition into TV sitcoms. Nobody would believe him as a family man — who’d have thunk it? And those of his ilk whose careers have lasted longest are the ones who evolve. Howard Stern survives because he’s actually a decent interviewer. Sarah Silverman made shock the backbone of her comedy in the early Aughts, until she evolved into an earnest bridge builder in a divided political climate. Anthony Jeselnik dabbled in shock for a while before fine-tuning the art of twistedness.  Daniel Tosh, the world soon discovered, isn’t a great stand-up comic but found his niche as YouTube veejay. And though Dane Cook’s name was once synonymous with plagiarism and sexist implosion, he’s still touring. The comedy world is full of men and women who rip into no-go areas with aplomb and leave little in the way of collateral damage. Katt Williams’ legendary bit on Michael Jackson slayed — and he did it while Jackson was still alive. (That was several minutes’ worth of hyperventilating laughter, circa 2006, deep in the George W. Bush era. Jeselnik crushed a rimshot line in under 280 characters back in March.) And then there’s Lisa "Lovable Queen of Mean” Lampanelli, who once said, "Political correctness has no place in comedy…You're supposed to make fun of things you're scared of, like racism." She did this by making lots of jokes about screwing black men. Lampanelli quit comedy in 2018, explaining that she feared her insult comedy was sending the wrong messages and might be hurting people. She is now a life coach. On the flipside, Clay recently announced he and Roseanne Barr are joining forces for a comedy tour. Because it’s 2019. * * * “Comedy's boundaries should be excellence,” Jon Stewart said during a 2018 interview with Christiane Amanpour. “So, whatever it is that you're talking about in terms of subject matter, if you're just napalming, you know, indiscriminately to provoke, then to me, that's not really comedy. Comedy should be something more human and truly believed.” Sitting at his side was Chappelle, who largely remained silent as Stewart answered Amanpour’s questions about #MeToo and whether there should be a way back for people like Chappelle’s masturbating comedian friend. Neither had suitable answers then — it was early days in this #MeToo headache — and we barely have any now. However, even the best of us suffer from a surfeit of exhaustion, to the point that we should probably stop expecting the “greats” to act the men they once were. I adore comedy, and I adored that Dave Chappelle, and I wouldn’t want him to be “cancelled” now, as if that’s even a danger. But it isn’t too much to ask Chappelle to be the thoughtfully provocative, excellent comedian he used to be. Or maybe that’s just outdated thinking. Today Chappelle’s “Sticks & Stones” is a performing wildly well among the far-right, the main perpetrators of the racism he still skewers and stands against. He’s also earned ample praise from Jackson loyalists, a portion of whom have been sending threats to the late pop star’s accusers and anyone who stands with them, including Oprah. They’re on board with the meanness. Chappelle is not obligated to comfort middle America. He isn’t angling for a sitcom, nor does he seem overly concerned about the ethics that inspired him to walk away from $50 million a decade and a half ago. As he frequently reminds his audience, he’s a rich guy who can retreat to his Ohio farm and live his life and never perform again if he chooses. And yet, even these actions show there’s still a little bit of 2006 Dave in there, the iconic comedian who told the legendary poet, “I need the people so bad. Because I need people so much for my process; something about celebrity interrupts the process. Because in order to do what I do, I have to be comfortable with making mistakes.” “Just saying,” he finished, “being nothing is more painful that watching a comedian grow.” And don’t we know from pain? Source: What happened to Dave Chappelle: The cruelty of “Sticks & Stones” is a sign of the times | Salon.com

    Read at 07:02 pm, Sep 10th

  • Amid Water Crisis, Michigan’s Top Health Official Said Flint Residents “Have to Die of Something,” Scientists Say | Flint's Deadly Water | FRONTLINE | PBS | Official Site

    Watch this clip on YouTube. As summer approached, Shawn McElmurry, Dr. Paul Kilgore and Dr. Marcus Zervos were growing increasingly concerned. The three men sat on a scientific panel that was formed to look into the source of a deadly outbreak of Legionnaires’ disease in Flint, Michigan. The spread of the disease coincided with the state switching the city’s drinking water supply to the Flint River — and it had gone on for more than a year before the public was notified. Now, it was May 2016, four months since Michigan Gov. Rick Snyder announced both the existence of the outbreak and the creation of the panel, and the state health department still hadn’t officially authorized the panel to begin working, its members say. The group knew how Legionnaires’ disease, a severe form of pneumonia caused by the waterborne legionella bacteria, operates: once the weather heated up, more people could die. So in May, they met with state health director Nick Lyon to warn him that it was urgent to step up monitoring for Legionnaires’ cases. “I remember, at one point, my colleague telling him that if he didn’t do that, people could die,” McElmurry, an engineering professor and the chair of the panel, told FRONTLINE. “Unfortunately,” McElmurry said, “Nick Lyon’s response was that ‘They’ll have to die of something.’” That’s just one of the troubling alleged incidents reported in new detail — or for the first time — in Flint’s Deadly Water. Based on two years of reporting, the FRONTLINE documentary premiering Sept. 10 reveals how a public health disaster that’s become known for the lead poisoning of thousands of children also spawned one of the largest outbreaks of Legionnaires’ disease in U.S. history. By the time of the May meeting with Lyon, Flint’s water supply had been switched following public outrage about lead levels, and the Legionnaires’ outbreak, which had killed at least 12 people, had subsided. As the film details, the panel members — who have never before spoken publicly about their experience outside of court testimony — say the state repeatedly tried to impede their efforts to identify the outbreak’s source and prevent a recurrence. In addition to his work on the panel, Zervos observed the outbreak’s impact up-close: He treated the youngest confirmed victim of the Flint Legionnaires’ outbreak, Jassmine McBride, in her final months. In the above excerpt from the film, Zervos told FRONTLINE that he was “flabbergasted” by how he says Lyon responded to the warning. “It was a situation where you’re just, I mean, you’re just in shock as a result of him saying that — the director of the Health Department,” he said. Nick Lyon declined to be interviewed. In a letter, his attorney said, “Director Lyon did not make that crass remark.” He said the team’s work was one of Lyon’s top priorities and blamed any delays on the scientists. While he remained in office, Lyon would eventually be charged with involuntary manslaughter for failing to alert the public and allegedly covering up the Legionnaires’ outbreak. Prosecutors also accused Lyon of interfering in McElmurry’s investigation. Through his attorney, Lyon has maintained that he did nothing wrong. Earlier this year, a new prosecution team dropped all charges against him. For the story of how the outbreak happened, why it continued for more than a year before state officials alerted the public, the consequences of that delay for the people of Flint, and the status of the legal effort to hold people accountable, watch Flint’s Deadly Water. With on-the-ground reporting from director Abby Ellis, reporters Kayla Ruble and Jacob Carah, and FRONTLINE Senior Editor Sarah Childress (all Michigan natives), the documentary sheds new light on an outbreak that has received little national attention. “Most people outside of Flint look at the lead issue as the main issue,” Flint city council member Eric Mays tells FRONTLINE in the documentary. “But the killer has been Legionnaires’… I still don’t think that they want people outside of Flint to know.” Flint’s Deadly Water premieres Tuesday, Sept. 10 at 10 p.m. EST/9 p.m. CST. Tune in or stream on PBS (check local listings), at PBS.org/frontline, and on the PBS Video App. Source: Amid Water Crisis, Michigan’s Top Health Official Said Flint Residents “Have to Die of Something,” Scientists Say | Flint’s Deadly Water | FRONTLINE | PBS | Official Site

    Read at 06:41 pm, Sep 10th

  • Killer Hallucinogenic Weed Blooming in Upper West Side Median Strip: Report - NBC New York

    What to Know According to West Side Rag, in the area of Columbus Avenue and 93rd Street, across from Trader Joe’s, highly toxic bush was found The plant is Datura Stamonium – also known as Jimson weed According to a Brooklyn Botanic Garden blog article, "all parts of the plant are toxic, most particularly the seeds" If you happened to come across a plant with trumpet-shaped flowers inside a concrete median strip along a bike lane on the Upper West Side you may have thought nothing of the unassuming plant. But, be warned -- for the plant could kill you! According to West Side Rag, in the area of Columbus Avenue and 93rd Street, across from Trader Joe’s, a highly toxic bush of Datura Stamonium – also known as Jimson weed -- was blooming. The plant is deadly when eaten -- even in small quantities.  On Saturday, Adrian Benepe, who served as New York City Parks commissioner for 11 years under Mayor Michael Bloomberg prior to joining The Trust for Public Land, tweeted: “What a long, strange trip: Bumper crop of Datura stramonium, aka Jimsonweed, growing in planting bed on Columbus Ave. Greenway at 93rd St. in NYC. A well-known hallucinogenic plant, it is also fatally toxic when consumed in even tiny amounts.” What a long, strange trip: Bumper crop of Datura stramonium, aka Jimsonweed, growing in planting bed on Columbus Ave. Greenway at 93rd St. in NYC. A well-known hallucinogenic plant, it is also fatally toxic when consumed in even tiny amounts. ⁦@NYC_DOT⁩ ⁦@nycHealthy⁩ pic.twitter.com/k7p9sy3p6Z — (((Adrian Benepe))) (@Adrian_Benepe) September 7, 2019 Benepe said the planting bed in question is under the jurisdiction of the NYC Department of Transportation, according to the West Side Rag. In a statement to News 4, the DOT said: "The City removed the plant today. DOT did not plant here and does not handle plantings at these pedestrian refuge islands. We are checking records for any local entity that has offered to maintain the area in the past." News 4 also reached out to the city's Department of Parks & Recreation for comment but did not immediately hear back. Benepe says it is low risk to touch the weed -- though he recommends leaving removal to the professionals. That is exactly what happened.  Top Tri-State News Photos By Monday early afternoon, the Parks Department uprooted the Jimson weed and carted it off.  According to a Brooklyn Botanic Garden blog article, "all parts of the plant are toxic, most particularly the seeds. Potent amounts of alkaloid compounds are present, which potentially cause convulsions, hallucinations, and even death if ingested. And as climate change increases the amount of carbon dioxide in the air, studies have found that the toxicity of plants like jimson weed only increases." endclickprintinclude Source: Killer Hallucinogenic Weed Blooming in Upper West Side Median Strip: Report – NBC New York

    Read at 02:11 pm, Sep 10th

  • by Phillip Newall

    Editor’s Note: The following is an excerpt from Philip Newall’s new book Further Limit Hold ’em; Exploring the Model Poker Game. It should be available in December. Editor’s Note: This article was first published in the November, 2012 issue.

    Read at 01:43 pm, Sep 10th

  • Democratic Socialists of America (DSA)

    The opening scene of the new Netflix film American Factory shows the 2008 closing of the G.M. auto plant in Dayton, OH, with more than 2,000 workers losing their jobs.  The plant sat idle for years until it was restarted by a Chinese company, Fuyao Glass, which makes automotive windows.

    Read at 01:27 pm, Sep 10th

  • When bosses play the victim

    Rachel Stafford takes on bosses who claim to be bullied by worker organizing. Recently, the president of the university I work for went on something of a speaking tour, meeting with all staff members to “listen” and “connect.

    Read at 01:23 pm, Sep 10th

  • How I negotiated a $300,000 job offer in Silicon Valley

    IntroductionFirst, some assumptions on my part:Your mileage may vary. I’m obviously not stating that everything I went through is easily reproduced. I am using it as a backdrop to color some advice that I think anyone totally new to this to keep in mind.You have offers, plural.

    Read at 01:18 pm, Sep 10th

  • Arthur Sabintsev

    Hello! What's your background and what do you do? Heyo! My degrees are in biological physics (BS) and nuclear physics (MS). I am a self-taught software engineer as I spent the majority of my teens and college years making websites and firefox browser extensions.

    Read at 01:15 pm, Sep 10th

  • Roots of the U.S.-China Trade Conflict

    The most anti-worker president in recent memory slaps big tariffs on products made in China, in the name of protecting the jobs of American workers. Corporate lobbyists criticize the tariffs — but say we must get tough on China’s trade policies.

    Read at 12:13 am, Sep 10th