Microsoft Tells FTC Repair Poses a Cyber Risk - <AUTHOR> https://securepairs.org/microsoft-tells-ftc-repair-poses-a-cyber-risk-it-doesnt/ <comment><USER2> Right to repair is just one of the many cases of "security" being used as an excuse to take away freedom, and it's one of the ways the corporations and governments (nearly the same thing, really) gradually build their dystopian vision of complete control over the population. It's so alluring, because "who doesn't want to be safe and secure?" On the surface it's appealing, but if you think about it, much like "a world without (cyber)crime", the ultimate result of striving for perfect security is dystopia.

Then, much as perfect freedom is unattainable, so is perfect security; and I think it's time we rebelled against this desire to achieve it. Unfortunately, it seems desiring _any_ sort of security is only going to give the companies more leverage to use it against us, so the way to go is to reject completely their notion of (centralised, authoritarian) security, and make _freedom_ the highest priority. Insecurity is freedom, and that is what we should fight for.

That infamous Benjamin Franklin quote has really taken on a deeper meaning recently.

<comment><USER3> Security against institutions is just as important as security against individuals, because big institutions aren't any more moral than the average individual. Everyone thinks about security against criminals, but the reason we don't live in a third world dictatorship is our constitutionally enshrined security against the police.

<comment><USER4> > Security against institutions is just as important as security against > individuals

This is very well phrased.

It gets across something I was having trouble putting into words -- that security and freedom aren't always just opposite goals that conflict with each other. Freedom isn't an ideal, it's a practical system designed to guard against tangible harms.

</comment></comment></comment><comment> awinter-py Technology needs an immune system. 'Tamper evident' is the right approach, but achieving this in a way that consumers can use is hard and requires building blocks that don't exist yet.

How can you verify that the motherboard you sent out is the one you get back _and_ it doesn't have extra hardware inside the case?

msft isn't wrong that creating a trusted supply chain for hardware is difficult, but I think that 'how can I trust the repair shop' and 'how can I trust MS' are the same problem.

<comment><USER5> The real threat is not tampering, the real threat is from the manufacturer and the corporations that already have keys to the data kingdom and control the entire stack. I have yet to hear of a single case of hardware tampering as software tampering is much much easier and infinitely more effective... and even if hardware tampering happens it's an extreme edge case and almost irrelevant.

<comment><USER6> This kind of blurs the line between hardware and software but there have been recent attacks where groups have written attack firmwares to motherboards and hard drives of cloud computing baremetal servers.

<comment><USER7> It's still software. It's just software that most application programmers ignore by inclination.

</comment></comment></comment></comment><comment><USER8> The argument they're making is obviously true, both in theory and in fact (look up how Best Buy repair technicians act as informants for the government).

> _if Microsoft wants to make devices that nobody can service and repair > without breaking their security model, they're entitled to do that. They can > make Surface Pros so hardened and tamper proof that merely opening them will > destroy them._

> _What they can't do is make devices that are repairable, and then lock out > everyone but their own service technicians._

There is no distinction here. The tamper proofing is what locks out everyone but their own service technicians.

<comment><USER9> > There is no distinction here.

Yes there is. The question is now "how does MSFT decide _who_ gets the right to repair their devices?"

If it's a reasonable and non-discriminatory process, then that might be fine, if they're arbitrarily deciding, then that's likely an issue the FTC _should_ investigate.

<comment><USER10> I need to get a repair authorized in 20 years. Can I still do it?

</comment></comment></comment><comment><USER11> Bought a Surface Pro 5th generation. Less than 8 months later the battery barely holds a charge. (<5 minutes) You're basically stuck because everything is glued together so you don't have any options other than to buy a brand new device. Feels a little like the forced Windows 10 updates. Good luck running Linux unless you are willing to live with all kinds of fail, due to poor camera support.

In the end it was far easier to just switch over to a Thinkpad.

<comment><USER12> The one year warranty should have covered your surface

I do support right to repair for the record. I just think this isn't a great example of how the right to repair would have actually helped someone

<comment><USER13> Battery warranty is one of the more grey areas of warranty coverage. Depending on local legislation and the specific coverage for the model you can get 6-12 months (it's considered a consumable) and the warranty might start when the battery was manufactured rather than the sale date (like Dell did some years ago, not sure if it's still a practice).

<comment><USER14> Consumables are readily replaced at low cost. Batteries (in phones/tablets) are essential central components.

If you need tools or instructions then the parts aren't really consumables.

<comment><USER15> Part of the issue is the trend of integrating parts that used to be easily replaceable. My first phone/laptop each had batteries that could be swapped in under 10 seconds with no tools. My latest do not.

</comment></comment></comment></comment></comment><comment><USER16> For those who thought Microsoft was an alternative to Apple with regards to right to repair: think again. The mid-range and high-end HP laptops (not Lenovo) are the easiest serviceable these days.

<comment><USER17> I use a hp elitebook 8470p i got for 100 usd from some guys garage and it is simply amazing. No screws, not even one to get the bottom cover off! You just slide it off and bam, your cpu/heatsink, fan, ram, and hdd are accessible on pretty much the same layer.

<comment><USER16> There are some sweet spots of old laptops which are serviceable. Some are even Macs. There's also some which have an Intel ME which can be disabled. Some are even x86-64. Its getting less common though.

</comment></comment></comment><comment><USER13> > If the TPM or other hardware or software protections were compromised by a > malicious or unqualified repair vendor, those security protections would be > rendered ineffective and consumers' data and control of the device would be > at risk

Then make it so the consumer can always tell if the security became ineffective.

<comment><USER18> I'm getting Vista flashbacks.

</comment></comment><comment><USER19> Ah, Theres the old Steve Balmer Microsoft we all know and love.

<comment><USER2> To be fair, Steve's Microsoft wasn't the one that introduced and popularised invasive telemetry, "treat the user like an idiot" design (including things like forced updates), nor the feverish desire to lock down the PC platform in the name of "security", but it may have just been a matter of time.

</comment></comment><comment><USER20> What about Tesla? IIRC they also prevent owners and third parties from working on their cars.

<comment><USER21> I can understand the rationale that modified software could be a major danger in their cars.

<comment><USER2> We've had modified cars and the associated culture for over a century, and no major danger has arisen from it (no more than the usual attributed to bad drivers, anyway.)

<comment><USER22> the batteries are fairly new technology though and do pose a serious hazard. i have no doubt as the field matures it'll become safer though, with established procedures. glad to see people like Rich Benoit are blazing the trail already.

<comment><USER23> Are the battery packs really more dangerous than playing with nitrous injection or race fuel? I don't need any certifications or permission to do either of those in my shed.

<comment><USER21> Is there a lot of information online relating to those subjects? I'd guess it's a lot easier to find knowledge in these areas than lithium ion batteries for cars, which obviously makes it more dangerous because there's nobody to help if something goes wrong.

</comment></comment></comment></comment></comment></comment><comment><USER24> Remember not to buy Microsoft if you have alternatives

<comment><USER25> You forgot to include Apple in your do not buy list.

</comment></comment><comment><USER26> Is there a way to replace board firmware with a hacked one that hides its alterations ny emularing ROM accesses? It could then also patch the secure boot checks out of the OS during system boot. Such a firmware would only be detectable by desoldering the flash chips and using a dedicated hardware reader.

If that is the case, every computer could be fundamentally compromised if you left it out of sight for long enough.

<comment><USER27> You don't even have to replace the board firmware. You can just replace the board, or the entire device, with one that does whatever you want. Like emulate whatever the real device does until the user enters their PIN and then send it back to the attacker via wireless.

There are a lot of different threat models here. The problem is that for the serious ones, like a state-level attacker, anything but continuous physical security is hopeless. But for the less serious ones, all of this faux spycraft is nothing but an excuse for anti-consumer behavior because the path of least resistance for your kids to get into your phone is by shoulder surfing your PIN, not using 0-days to install custom device firmware etc.

</comment></comment><comment><USER28> Is this the "new, open-source friendly" Microsoft?

<comment><USER29> Seriously. It's hard to understand why Microsoft has been getting so much fanfare from the OS community recently.

<comment><USER30> New generation of programmers, many of which would have been very young/not alive when Microsoft were at peak (anti-OSS)/EEE?

<comment><USER31> I am "one of them" Microsoft still has some old corporate people still in charge versus some of the new projects which are not managed necessarily by the same groups. Until all these types leave it will feel like Microsoft is passive agressive.

<comment><USER32> Blame it on the "old people" but you soon you will see that "young people" will pull the same nonsense as soon as they have to hit quarterly numbers. Big companies can't be your friend.

<comment><USER33> Microsoft's new strategy has not happened to the detriment of quarterly numbers, on the contrary.

</comment></comment></comment></comment></comment></comment><comment><USER34> As usual, DRM proponents try to sell DRM as a security feature. In practice it's the opposite. DRM is the security risk.

<comment><USER35> This sort of of statement is one I made back in the lates 90s and early 00s. As an absolute statement it was wrong and still is wrong, and I think the enthusiast tech community's adoption of it has done a huge amount of harm. We reflexively opposed things like TPMs at all, rather then recognizing that the real question is who controls putting keys there. It would have been (and still would be) better to fight to make sure the standard is that the end people in charge of the device have the right to control the master keys on it. By going for a blanket "no chains of trust at all" we gave up a lot of useful capabilities and left the door open for companies to come in making use of them but with unpleasant and unnecessary extras tacked on.

<comment><USER36> > This sort of of statement is one I made back in the lates 90s and early 00s. > As an absolute statement it was wrong and still is wrong, and I think the > enthusiast tech community's adoption of it has done a huge amount of harm.

It's absolutely a correct statement from a user perspective.

</comment></comment></comment><comment><USER25> I see no difference between Microsoft's position and that of Apple. Except that Apple is a trendy brand.

<comment><USER37> Yes they're both on the wrong side of right to repair.

</comment></comment><comment><USER38> Microsoft love Open Source.

User freedom, not so much.

</comment><comment><USER35> Microsoft isn't entirely wrong, and "right to repair" advocates conspicuous failure to acknowledge this tradeoff is one of the weaker and more irritating parts of the movement. Note _tradeoff_ as the word, it's not as if good things don't come with it too, nor does it mean the same weighting applies to everyone. It doesn't mean that those who want to shouldn't be able to hack on their devices. A similar tradeoff applies to pure software signing, where I am very much in favor of a legal requirement that hardware "possessors" [1] should be able to load their own master signing keys into any cryptographic roots of trust, and thus be able to run software of their choice with no further relationship with the manufacturer even in a system that has full signing required.

But the power to do that is also the power to screw it up. If any 3rd party can repair something like biometric hardware, any 3rd party could also compromise said hardware. Some people may find the benefits of the former outweigh the latter, but I do think it should be a choice in the law, and that liability should adjust accordingly. Ie., you can upfront on buying a device request that it not enforce cryptographic reqs for hardware repairs (this could be controlled by a permanent fuse or many other ways), but alternately you can also request that continue to be a requirement. In my case for example I _do not want_ to buy an iPhone that is "right to repair enabled". I don't want the magic circle of who can mess with it expanded any further than the minimum, which is Apple themselves who are by definition inside the tent anyway.

I also think the "right to repair" movement is a symptom rather then cause thing, and obscures the real problem for the vast majority of the market. The true core issue is that, in America in particular, legal standards for fitness for purpose are simply way, way too loose. The reasonable expectation most people have for buying something expensive is that it'll be in working order for a proportionate number of years. Not forever of course, but a $200-400+ bit of electronics shouldn't be dead in 13 months either. A $500-1k one should probably last at least 4-5 years without further cost, etc. The retail price should reflect whatever it takes to make that happen. Manufacturers are the ones in a position to deal with that, who have the best stats on failure rates, and can make decisions about tradeoffs between cheapness of repairs, more on QA, where more expensive repair reqs might reduce repair need even farther in the first place (or provide other feature value), etc. It simply shouldn't be the consumers' problem at all, beyond evaluating the retail price. Yet it's completely standard to have something ludicrous like a single year warranty.

Essentially, it's a classic market Externality problem. Everyone does expect their devices to last, but are forced to roll the dice on whether they specifically have bad luck and have to pay expensive rare repairs all by themselves. The manufacturer gets to advertise an artificially low price but externalizing the failure rate percentage onto the customers, and even charge extra for what should be standard. Instead warranties should be adaptive, something like "1 month for every $12 retail price up to a maximum of 5 years" say. Then let the market sort it out from there. I'm worried "right to repair" will ultimately be camouflage that let manufacturers skip out on their real responsibilities in some cases. Right to repair could make out of warranty repairs cheaper, but _you shouldn 't have to pay at all_ within a reasonable normal life.

\----

1: which would be defined as something alone the lines of "anyone who has paid, either upfront _or_ on an ongoing plan basis, for hardware and possesses the right to control access to it." Lawyers could make it watertight, but basically a definition which would explicitly not allow any sort of "oh we were only leasing it to them!" loopholes.

<comment><USER39> You are deluding yourself if you think cryptography can be the solution to an attacker with access to the physical machine. No crypto in the world can help you if I simply bug the fricking physical keyboard!

<comment><USER35> > _You are deluding yourself if you think cryptography can be the solution to > an attacker with access to the physical machine._

Of course it can be part of a solution. Responses like yours, which display zero consideration of threat scenarios, time/info/resource costs, or any potential improvements are somewhat tiresome these days.

> _No crypto in the world can help you if I simply bug the fricking physical > keyboard!_

I'm sorry, can you point to that "fricking physical keyboard" on my iPhone? I'll wait.

And even if you want to talk purely about PCs and attached keyboards, there is no inherent reason those couldn't be locked down too were it a general problem, and have a system refuse to trust any peripheral by default. HSMs can also be part of an overall plan that can mitigate some damage even from a keyboard bug. Furthermore, breaking into a private residence or business is a significantly more time/information/resource intensive problem then subverting a centralized repair place through which lots of hardware passes and is left unattended, and which will not in fact generally include peripherals (if my computer is broken, I bring just the computer in, not the keyboard/mouse/monitor).

<comment><USER18> _To get it out of the way, I disagree with the GP._

If you've looked through any number of the NSA or CIA leaks (Vault 7, for example) you'll have seen a device that looks like a standard Ethernet magjackn+ USB connector that has a little tiny processor embedded inside of it (it leaches power from the USB power rails, IIRC) that can be used to exfiltrate packets and the like.

It's not impossible to get bugged when you send your PC in for repair. It's also probably unlikely considering that there's many other easier ways to grab your data.

<comment><USER35> > _If you 've looked through any number of the NSA or CIA leaks (Vault 7, for > example) you'll have seen a device that looks like a standard Ethernet > magjackn+ USB connector that has a little tiny processor embedded inside of > it (it leaches power from the USB power rails, IIRC) that can be used to > exfiltrate packets and the like._

I don't see how that's related? PCs right now don't make use of the kind of cryptographic lockdown iPhones do. In the case of desktops that's likely because it's assumed physical security can be taken as more of a baseline, though in the case of notebooks it's probably more just inertia. The whole "right to repair" movement is precisely because this has been changing. I mean, what you wrote directly argues the case for locking down of hardware, encrypting everything along the busses between chips or having destructive tripwires or both, and so on.

> _It 's not impossible to get bugged when you send your PC in for repair. > It's also probably unlikely considering that there's many other easier ways > to grab your data._

The nature of security though is that it's worth being forward looking to what will come down the line in the future. _Particularly_ if we're talking about legislating it and putting the force of law behind making one decision or another, which tends to then be quite difficult to change. Isn't it worth being cautious about?

</comment></comment></comment></comment></comment><comment><USER40> God I hate this argument (even when I worked at Apple). There are many valid (eg remotely plausible) reasons, but "haxors cybering your security" is not one of them.

[autocorrect wanted to change haxors to hackers. I'm not sure if that's good or not)

<comment><USER40> People seem to hate this comment.

Here's what I mean by plausible: "If we filled it entirely with resin and glue it is less likely to break and hence less likely to need repair, but doing that makes repair impossible"

I do not mean _good_ reasons, I am specifically saying that there are "reasons" that are more plausible than "security"

</comment></comment><comment><USER41> It's time we also remove the "Windows"-button (edit: the branded concept, not the physical button) from all the keyboards. We should have the right to repair in the form of also removing corporate advertising from our devices.

<comment><USER42> Come again? The "Windows"-button is just a super/command key that exists on many other keyboards and OSes outside of Windows.

<comment><USER43> As I understand, it's only about removing the Win logo from the button, which is long due on officially Linux-supporting laptops.</comment></comment></comment>


VLC desperately needs a Mac OS developer to help out - <AUTHOR> http://forum.videolan.org/viewtopic.php?f=12&t=59905#p228791

<comment><USER2> This is really tragic. I rely on VLC for all my video viewing needs. It loads video much faster than QuickTime w/ Perian and is much more responsive, not to mention the vast array of file formats it supports. I also like being able to tweak the buffer sizes to optimize playback from my NAS. I'm a little shocked there aren't any Cocoa devs wanting to work on this. Do I need to try and learn Objective-C now? I fear I would only damage the code base.

<comment><USER3> MPlayer. I had some videos that wouldn't play with VLC that would play with MPlayer. (They were encoded with Nero Recode.)

<http://www.mplayerhq.hu/design7/dload.html>

<comment><USER4> mplayer is great and I use it as my primary video player, but I can understand why one would prefer VLC -- it's much more friendly, even if you use the GUI for both.

Also, mplayer and VLC are helpmeets in my system; sometimes VLC can play something that mplayer will choke on, and vice versa. VLC is a valuable resource indeed.

</comment></comment></comment><comment><USER5> Perian and QT have definitely not replaced VLC at all. I'll give you one example: VLC's filters kicks the functionality that QT can give square in the nuts. I often apply VLC's Audio tempo scalar synched with rate filter for listening to sped up lectures. That feature alone makes VLC necessary for me, not to mention all the other stuff it does.

<comment><USER6> VLC is also used by other programs (such as Handbrake) to perform decryption of protected DVDs.

<comment><USER7> Technically it's not. Programs use the libdvdcss functionality built into VLC, but there are other alternatives (such as Fairmount) that also use libdvdcss. VLC is just more likely to be installed. Indeed, if you're running 64-bit Handbrake then the lack of a (stable) 64-bit version of VLC makes Fairmount a better option.

</comment></comment></comment><comment><USER8> """Finally, we have a few issues, since Apple doesn't want us on the Mac platform and is blocking us a lot, and refuses to explain why."""

I don't understand this. The Mac is pretty much an open platform. Putting video on the screen is not something Apple can block.

Does anyone know what they are specifically talking about?

<comment><USER9> This sentence means that _some people_ in the Apple compagnie doesn't really like VLC. So they remove VLC from the offcial Apple download page without any explanation. We don't understand why as some other people in Apple do really like VLC...

<comment><USER10> The answer is simple really. They want to:

A) push their now-free QuickTime X player, along with the H.264 codec.

B) push consumers towards consuming more video through iTunes instead of other, um, ad-hoc channels.

I love VLC, but I can see how Apple has a competitive interest in not actively promoting VLC.

</comment></comment></comment><comment><USER11> Do enough people care to fund it? I personally do use VLC, but if it became unmaintained I'd just switch to another player.

I'm (theoretically) willing to maintain it if I can make enough in donations to pay the bills. (Contracting fills that role right now.)

<comment><USER12> That's probably the spirit that got VLC where it is now. Not having the time or the skill to contribute is one thing, being willing to contribute but only if there's money involved is another.

<comment><USER13> If people would prefer to use something else than pay for VLC, then VLC is literally not worth it.

It's quite possible that's the case; I think most people on Mac would rather use Quicktime (w/ Flip4Mac [ _1_ ], which is Microsoft-supported) for free than pay for VLC. But if there were a trivially easy way to pay a dollar, I think a lot of folks would. Ultimately, open source projects that aren't fun to work on will live or die by the micropayments problem.

 _1_ or whatever; I'd never heard of Perian before this thread, and I just switched back to Linux from OS X.

<comment><USER14> "It's quite possible that's the case; I think most people on Mac would rather use Quicktime (w/ Flip4Mac [1], which is Microsoft-supported) for free than pay for VLC."

I'm probably an atypical Mac user. I sometimes use my Mac to watch movies, but generally don't care for the OSX UI and don't care if something doesn't have a true OSX feel (n fact, non-OSX may suit me better). (I have a Mini for development purposes but do most things on Kubuntu.)

I like using VLC because, aside from its feature set, I use it on Vista and Kubuntu as well and know what it can do and how to do it. (Usually; for whatever reason, the default keyboard shorts are oddly different on different platforms. I don't _think_ it's because of collisions with preexisting shortcuts, and I usually just reassign them to suit myself.)

This thread has informed me about some other OSX options I'll try out, but so far VLC is about the most full-featured player I've seen. Couldn't live without the equalizer, gamma/brightness control, audio sync adjustment, and such.

<comment><USER13> There are other differences, too. The playlist showed up by default on the OS X version of VLC, and I haven't found a way to make that happen on the Ubuntu version. Also, the OS X version has a very nice "jump 10 seconds" feature which the Ubuntu version seems to lack entirely (or I couldn't find it). It's weird that the OS X version of VLC actually seemed to have more features and a markedly better interace than the linux version, to the point that although I used VLC almost exclusively on OS X, now that I use linux I usually use Totem.

<comment><USER14> Not sure about the playlist thing, but on linux there are keyboard shortcuts for jumps of varying lengths (some combo of left or right arrow and ctrl|alt|shift variations).

</comment></comment></comment></comment></comment></comment><comment><USER15> It would be really sad if VLC on Os X goes , that's one of the main apps i use.

QT + Perian and MPlayer are nowhere near to VLC in terms of functionality. I can get any ol format and get VLC to play it , as an example .iso files.

</comment><comment><USER16> Interesting that it took 10 days for this news to get out. Kind of makes me feel like Mac OS X isn't exactly a high priority for the people behind VLC?

I do hope that they find a dev though.

<comment><USER17> The people behind VLC are whoever shows up, so I'd say VLC isn't a high priority for OS X open source volunteers.

<comment><USER18> That's a shame because VLC is by far the best media tool I've ever used on OS X in terms of playback compatibility.

<comment><USER19> I think that largely used to be true ... but nowadays with tools like Perian and Mplayer OSX Extended it's definitely no longer the case. With Snow Leopard, VLC is actually the weakest of the media players in terms of stability and performance, in my opinion.

<comment><USER20> Comment grandparent explains why.

<comment><USER21> Not the original commenter, but here is my take on it on Snow Leopard: I use QuickTime X a lot for its h.264 hardware acceleration. As of now, that's the only option you have if you want to play 1080p on Mac. Coupled with Perian, I think it solves the majority of playback issues with a rather simple user interface. Then I use MPlayer OS X Extended for its better support for MKV, subs, DTS, AC3, etc. And it has a very simple interface like the original QuickTime player (< X). So really, there's much less need for VLC on that platform...

</comment></comment></comment></comment></comment></comment><comment><USER22> "Taking into account the learning curve to VLC development, I think it is fair to say that the situation is now critical."... Yikes.

I'm a big fan of VLC, and hope to see someone jump on board with them. VLC has an amazing suite of tools under its hood, although most casual users simple reference them it as a Media "Player". If I knew a hint of Mac OS development, I'd be jumping on board.

<comment><USER23> The OP:

VLC is looking for MacOS X developers Postby ajmas on _Fri May 29, 2009_ 5:55 am

<comment><USER24> by Rémi Denis-Courmont on _Sun Dec 06, 2009 11:00 pm_

There are now effectively zero active developers for MacOS.

<comment><USER9> This is absolutely false. The only thing true is the fact that there isn't so many mac developers for VLC. They need helps because they want to bring a really nice player.

</comment></comment></comment></comment><comment><USER25> They should start a kickstarter project to pay someone to work on it..

</comment><comment><USER26> I wonder if this is because Perian has eliminated much of the need for VLC.

<comment><USER27> Quicktime can't crank the volume up like VLC does. It sounds like a small thing, but when you're on a laptop with little speakers it makes a big difference.

<comment><USER28> Yeah it can. There are two ways, in the sound section of the video properties ("boost") or by holding shift and pressing the up-arrow key. Normally the QuickTime volume control widget stops you at 0db (any more than that and you would be clipping the signal) but if you hold shift and keep pressing up (up and down control the volume widget) it will go beyond 0db, even though it doesn't visually indicate it.

<comment><USER29> Sadly, they disabled this in QuickTime X. Some of my friends use older versions of QuickTime to get around this problem.

<comment><USER30> Geez, os x is completely riddled with tricks you 'have to know', usually achieved by some obscure key combination. Which UI book did they get this out of ? ...

<comment><USER29> The don't-clutter-my-screen-with-nine-hundred-thousand-buttons-and-menus book?

Joking aside, most OS X keyboard shortcuts actually follow a very rational scheme (which is mentioned in the Apple HIG, IIRC). If Cmd+X performs an action X, both Cmd+Shift+X and Cmd+Opt+X will perform similar versions of X, so you don't have trouble remembering "special" shortcuts.

Example: Cmd+Q quits an application, Cmd+Shift+Q logs you out and Cmd+Opt+Shift+Q logs you out without confirmation.

Another example: Cmd+Opt+Esc brings up the Force Quit dialog. Cmd+Shift+Opt+Esc force quits the current application without confirmation.

I don't think this is an OS X specific thing. I suspect Windows, KDE and GNOME all follow a similar HIG.

(No, none of those shortcuts are good for your RSI.)</comment></comment></comment></comment></comment></comment>


AT&T's purchase of T-Mobile will test antitrust law - <AUTHOR> http://venturebeat.com/2011/03/20/atts-purchase-of-t-mobile-will-test-antitrust-law/ <comment><AUTHOR> Really need some experienced attorneys and former regulators to weight in on this critical question but Dean Takahashi at least gives it a start. Consumer advocates, like CFA's Mark Cooper, have smart things to quote and always oppose these deals but they do not often prevail in Washington.

</comment><comment><USER2> So does this mean all of the hacked iPhones on T-Mobile are now legit?</comment>


Writing type parametric functions in Go - <AUTHOR> http://blog.burntsushi.net/type-parametric-functions-golang

<comment><AUTHOR> If you want to skip to the more interesting tidbits, see the `Examples` section for the github.com/BurntSushi/ty/fun package docs [1].

[1] - <http://godoc.org/github.com/BurntSushi/ty/fun></comment>


Types - <AUTHOR> https://gist.github.com/garybernhardt/122909856b570c5c457a6cd674795a9c? <comment><USER3> Great overview article but I have a comment.

> In Idris, we can say "the add function takes two integers and returns an > integer, but its first argument must be smaller than its second argument":

> If we try to call this function as add 2 1, where the first argument is > larger than the second, then the compiler will reject the program at compile > time.

> Haskell has no equivalent of the Idris type above, and Go has no equivalent > of either the Idris type or the Haskell type. As a result, Idris can prevent > many bugs that Haskell can't, and Haskell can prevent many bugs that Go > can't. In both cases, we need additional type system features, which make > the language more complex.

I really wish when people talk about dependently typed programming languages (e.g. Idris, Coq, Agda, ATS), they would mention the effort involved to get these compile time checks to happen.

These languages allow you to capture almost any program property you can think of as a type. Instead of being limited to saying function F "returns a number/list/string" like common type systems, you can capture arbitrarily complex properties like "F returns an even number", "F returns a permutation of the input list", "F always halts" or "F returns a proof of Fermat's Last Theorem". You don't get this for free and you have to help the computer verify these.

Proving F matches the type is arbitrary hard and is impossible to automated in general (e.g. the halting problem tells us we cannot always tell if a function halts). For example, consider the type "F returns a sorted permutation of the input list", where F could be an implementation of bubble sort, quicksort, merge sort, radix sort or something even more complex. Each algorithm will require a different and potentially complex proof and most of the time the programmer needs to help the computer find the proof. It's completely unlike what people are used to from type systems and is an even more massive leap than for someone moving from the type systems of e.g. C++/Java to Haskell.

I'm glad people are more aware of dependently typed languages but I feel the immense jump in how practical they are to use is severely overlooked.

<comment><USER4> >You don't get this for free and you have to help the computer verify these.

For library types, the most commonly used properties will have already been proven; you wouldn't have to write a proof that "sort" sorts a list any more than you'd have to write the sort function itself.

The computer can also be surprisingly good at proving these things in many cases. E.g. I was playing with the proof assistant Isabelle recently, and I tried to prove that given an int, a proof that twice that int is greater than 13, and a proof that four times that int is less than 29, then that int must be 7. I thought I'd have to fiddle around with induction on Peano numbers and the like, but nope, it could be proved with just a single "by arith".

<comment><USER5> > it could be proved with just a single "by arith".

Tactics are probably a usability improvement for mathematicians who are used to proving everything by hand. But for programmers used to type inference, they're a step backwards. Mathematicians typically prove much deeper results, but they do so at a much slower rate than programmers write programs.

<comment><USER6> Huh tactics are pretty great. Say exactly what you want and the computer programs itself!

It's unfair to just compare development time between tactic-generated programs in a dependent language with manually written programs in a non-depenendent language. The end result in the dependent language is much more valuable.

<comment><USER5> I'm not comparing dependent types vs. no dependent types. I'm comparing higher-order dependent types (Agda, Idris, Coq, etc.) vs. first-order dependent types: [https://news.ycombinator.com/item?id=12350147](https://news.ycombinator.com/item?id=12350147)

Seriously, Coq-style proof scripts are utterly unredable when they grow past a certain size. The only way to understand them is to replay them, so that you can see the intermediate hypotheses and goals.

<comment><USER7> I agree. Note that the Coq-style tactics-based approach is not the only possibility.

In Agda, you are expected to write proofs in a functional style, similar to Haskell programs. There is a small amount of integrated automation, which helps you fill in holes in your proofs — however, the results are explicit proof terms, inserted at the right place in your program.

Systems which behave in this fashion have the de Bruijn criterion, as described in Geuvers (2009) "Proof assistants: History, ideas, and future".

[https://www.dropbox.com/s/4mwtxojg7yqb365/Geuvers2009.pdf](https://www.dropbox.com/s/4mwtxojg7yqb365/Geuvers2009.pdf)

<comment><USER5> Thanks for the links (this one and the one from your other reply), I'm checking them.

</comment></comment></comment></comment></comment></comment></comment><comment><USER8> The Idris example seems to need further explanation:

> In Idris, we can say "the add function takes two integers and returns an > integer, but its first argument must be smaller than its second argument":

> add : (x : Nat) -> (y : Nat) -> {auto smaller : LT x y} -> Nat

> add x y = x + y

That's all well and good, if you know the values of x and y at compile time. Consider a program that reads x and y from STDIN. The user could provide an x that is equal to or larger than y (or could provide only one value, or values that are not numbers). I see no way to deal with that except to throw a runtime error. Is that what would happen?

<comment><USER9> The missing explanation is that `smaller` is a third argument to the function. It's type is a proof that x <= y. Since it is in curly brackets with the auto keyword, the compiler will fill in this proof in many cases, like when the values are statically known.

In the case where the values are read from the external environment, first you would have to compare them before calling the function. The comparator would return either a proof that x <= y, or x > y. In the first case you plug that value into the `smaller` argument, in the second case it's your responsibility to signal whatever kind of application-specific error is appropriate (assuming x > y is some kind of erroneous condition).

<comment><USER8> What would happen if you did not compare them before calling the function, and just passed them in such that x >= y?

<comment><USER10> Nothing; it would return a partially applied function that waits for a proof that `x < y` before continuing. (Trying to use that function as an integer would be a type error).

The key here is that `add` is _not_ a function `Nat -> Nat -> Nat`, it is a function `(a : Nat) -> (b : Nat) -> LT a b -> Nat`. There are just some compiler features that allow you to avoid having to write out the full `LT a b` proof each time you want to add things!

<comment><USER11> What's even more interesting is the structure of "LT a b". Let's assume that Nat has the common Peano arithmetic structure:

        data Nat : Type where
          Zero : Nat
          Succ : Nat -> Nat

Here "Zero" represents zero and "Succ" represents "one more than". Hence "Succ (Succ (Succ Zero))" is one more than one more than one more than zero, AKA three.

Given two such values "x" and "y", how on Earth can we prove that "x < y"? What would that even look like?

Well, there's a really obvious case: we know that "x < Succ x", so we can define a value "Obv" which represents this:

        Obv : (x : Nat) -> LT x (Succ x)

Notice that we don't write "y" explicitly, since it can be written in terms of "x".

What about those cases where "x" and "y" differ by some other amount? We could define values for "x < Succ (Succ x)", "x < "Succ (Succ (Succ x))", and so on, but that's clearly redundant and inconvenient. Instead, we just need to spot that all of these fit the pattern "x < Succ y", where "x < y". This lets us argue by induction: if "x < y", then "x < Succ y", which we can represent using a value "Ind" like this:

        Ind : (x : Nat) -> (y : Nat) -> LT x y -> LT x (Succ y)

This is enough to prove that "x" is less than any number greater than "x". In fact, we don't need to give the values of "x" and "y" explicitly, as they appear in the "LT x y" types and can hence be inferred. In languages like Idris we indicate inferrable parameters using braces, hence our "LT" type looks something like this:

        data LT : Nat -> Nat -> Type where
          Obv : {x : Nat} -> LT x (Succ x)
          Ind : {x : Nat} -> {y : Nat} -> LT x y -> LT x (Succ y)

Now, this looks familiar. Compare it to the definition of "Nat": we have two constructors, one of which ("Zero"/"Obv") can be written on its own, whilst the other ("Succ"/"Ind") is recursive, requiring an argument containing the same constructors.

Values of type Nat include:

                   Zero  : Nat
              Succ Zero  : Nat
        Succ (Succ Zero) : Nat

And so on, whilst values of "LT x y" include:

                 Obv  : LT x (Succ x)
             Ind Obv  : LT x (Succ (Succ x))
        Ind (Ind Obv) : LT x (Succ (Succ (Succ x)))

Although LT contains more static information than Nat, it actually follows exactly the same structure. What does this mean? Values of type "LT x y" _are numbers_ ; in particular they're the _difference between "x" and "y"_!

In the case of "LT", these numbers start counting from one (since one number is not less than another if their difference is zero). If we define a similar type for "x <= y" it would count from zero, and the same can be done for ">" and ">=".

These types are actually really useful. They're also closely related to linked lists, vectors, etc. although they store dynamic information as well as static.

I assume this is all old hat to those with mathematical training, but I found it interesting enough to write about at [http://chriswarbo.net/blog/2014-12-04-Nat_like_types.html](http://chriswarbo.net/blog/2014-12-04-Nat_like_types.html)

</comment></comment></comment></comment></comment><comment><USER12> Decent overview of many concepts, but the opening line isn't strictly true:

> A type is a collection of possible values.

A type is a proposition, and "This binding has one of these possible values" is merely one type of proposition. So a type is much more powerful than a simple set-based interpretation, even though this is how most people think about it.

For instance, in Haskell you can encode a region calculus that ensures prompt reclamation of file handles and other scarce resources [1]. The relations between nested regions to ensure correctness seems awkward to interpret as a set, but it's straightforward if you just think of types as a relation.

[1] [http://okmij.org/ftp/Haskell/regions.html#light- weight](http://okmij.org/ftp/Haskell/regions.html#light-weight)

<comment><USER13> (Static) types may be seen as propositions, but this is just one interpretation of them. We can also interpret types as sets (although this doesn't work out very well except in very simple languages), as domains (most commonly), as cpos, as relations over closed terms (PERs), and so forth.

The types-as-propositions interpretation is a particularly useful one because it lets us take ideas from the field of formal logic and apply them to programming. But it is only _one_ interpretation, and _not_ a definition of what types are.

I don't think it's possible to give "type" a good, short definition. For me John Reynolds' definition comes closest: "Type structure is a syntactic discipline for enforcing levels of abstraction." But that's rather a mouthful, and not a good introduction to what types are.

> A type is a proposition, and "This binding has one of these possible values" > is merely one type of proposition.

The type that an expression or variable has does _not_ correspond to the proposition that that expression has a certain set of possible values! You're mixing the object-level (the language & its types) with the meta-level (assertions about the language). This is a total misunderstanding of Curry- Howard. Rather, the values an expression might take on can be interpreted as _proofs_ of the corresponding proposition.

<comment><USER12> > (Static) types may be seen as propositions, but this is just one > interpretation of them. We can also interpret types as sets (although this > doesn't work out very well except in very simple languages), as domains > (most commonly), as cpos, as relations over closed terms (PERs), and so > forth.

Right, I wasn't advancing the propositional definition as the only one, merely as one that sufficiently captures the generality, because the article's current definition of types-as-sets is insufficiently general.

<comment><USER5> Under HoTT's take on propositions as "types where any two inhabitants are equal", "types as propositions" is even less general than "types as sets".

As I stated in a comment on that gist, what types denote varies from one type system to another: [https://gist.github.com/garybernhardt/122909856b570c5c457a6c...](https://gist.github.com/garybernhardt/122909856b570c5c457a6cd674795a9c?#gistcomment-1856634)

<comment><USER12> No one except mathematicians and type theorists are using HoTT, where every programmer in existence is using first-order and sometimes second-order logic. I think it should be obvious which is more appropriate if this is supposed to be an introduction.

The idea that types express propositions in first- or second-order logic about a program one is writing is sufficiently general for every programmer to understand the proper scope of type checking. Types-as-sets is simply false as an introductory concept.

<comment><USER5> > No one except mathematicians and type theorists are using HoTT,

Right.

> where every programmer in existence is using first-order and sometimes > second-order logic.

I'm not so sure about this one. Perhaps by "programmer" you mean "every Haskell programmer"? Even then I'm still not sure. Are you counting "let's approximate `forall x. P(x)` with the large but finite conjunction `P(a) /\ P(b) /\ P(c) ...`" as "using first-order logic"?

<comment><USER12> I mean pretty much every programmer, even C programmers. Simple business logic is rife with such propositions. Note that I didn't say they use first-order logic to encode invariants in types or anything, but they obviously use it everyday to write programs

Haskell programmers also use higher order logic, which is also common now in Java and C#.

<comment><USER5> > Note that I didn't say they use first-order logic to encode invariants in > types or anything, but they obviously use it everyday to write programs

I wasn't talking about types either. Most programmers I've talked to simply can't use formal logic.

<comment><USER12> Hmm, I don't think you're being very charitable. That these programmers can write working programs suggest they sufficiently understand disjunction, conjunction, negation, conditionals and quantification.

Perhaps they have trouble abstracting the semantics from their preferred language and/or syntax, but that doesn't mean they don't grasp their meaning.

<comment><USER5> I offer this thread as evidence: [https://news.ycombinator.com/item?id=12342583](https://news.ycombinator.com/item?id=12342583)

<comment><USER14> Hey, it's not the fault of the programming masses that some evil motherfucker conflated implementation extension (inheritance) and subtyping.

Subtyping = "A <: B, therefore I can start with an A, forget the details irrelevant to B, and just use it as a B."

Inheritance = "A inherits B, if I try to actually use it as a B, it promises to work. However, it might actually blow up in my face, because it was keeping its extra details secret from me and promising they wouldn't blow up."

<comment><USER5> > programming masses, some evil (bleep)

Too harsh. Logic is hard for humans. Evolution has trained us to take lots of mental shortcuts, not to be perfect calculating machines. Even the most technically competent people rely on their intuition. What differentiates them from the rest of us is that they have trained their intuition to make it more reliable [0].

It's the job of a language designer to arrange things so that intuition doesn't mislead programmers regarding the meaning of language features. Before proposing a new feature, the designer must anticipate how programmers will think about code that uses this feature, and there better not be a mismatch between what programmers think the code means and what the code actually means.

> A inherits B, if I try to actually use it as a B, it promises to work.

There's nothing wrong with implementation inheritance per se [1]. It addresses a very common use case: you have a thing Foo, and you want to produce another thing Bar that's similar to Foo, with minor changes here and there. This describes accurately how many things work in real life. However, you can't assume that Bar can always be used where Foo is expected. Those "minor changes here and there" might have rendered Bar incompatible with Foo. Hence, "inheritance is not subtyping". As stated above, it's the job of a language designer to anticipate this.

[0] [https://terrytao.wordpress.com/career- advice/there%E2%80%99s...](https://terrytao.wordpress.com/career- advice/there%E2%80%99s-more-to-mathematics-than-rigour-and-proofs/)

[1] Other than the lack of mathematical elegance, but few things are mathematically elegant in life.

<comment><USER14> >Hence, "inheritance is not subtyping". As stated above, it's the job of a language designer to anticipate this.

Yes, it is their job, by _not allowing subclasses to be used as subtypes._

<comment><USER5> Some subclasses can be safely used as subtypes. For example, those that meet the following two conditions:

(0) Not directly mutating superclass fields. (Reading them is fine.)

(1) Not overriding non-abstract methods of the superclass.

<comment><USER15> If classes are types, subsclasses should be subtypes -- so the above restrictions should just be applied to subclass relationships.

One could also have an non-subclassing mechanism of implementation sharing, that might be declared something like this:

      class Related does (method1, method2) like Base;

_or_

      class Related does all except (method3) like Base;

<comment><USER5> That syntax is too heavyweight (IMO). AFAICT, most of the time, when people use inheritance, they don't want to define perfect subtypes. They just want to reuse as much already implemented behavior as possible.

<comment><USER15> > That syntax is too heavyweight (IMO).

I'd be fine with something more compact for the default case of "steal all behavior except what is explicitly overridden", such as:

      class Related like Base;

<comment><USER5> (0) Do you have a use case for cherry-picking which methods are inherited? If not, it seems like too much complexity for me.

(1) Do you think there would still be a use case for "extends"? If not, I'd rather drop it as well.

<comment><USER15> > Do you have a use case for cherry-picking which methods are inherited?

The most obvious is that it makes intent explicit in the case of multiple similarity (I prefer "similarity" to "inheritance" for non-subtyping implementation reuse.)

It might in some cases make sense to do this on an _interface_ rather than (or in addition to) _single method_ level; there's probably a lot to work out in the ergonomics of non-subtyping implementation reuse.

> Do you think there would still be a use case for "extends"?

Sure, classical subclassing (subtyping with implementation sharing) remains an important use case. It might be desirable to statically verify certain guarantees (some have been suggested in at least one subthread of this thread by another poster) to assure that the subtyping is reasonable, but even without such verification the use case remains important.

<comment><USER5> > The most obvious is that it makes intent explicit in the case of multiple > similarity

Keep in mind that similarity is explicitly intended to be a quick-n-dirty form of code reuse. If the programmer has enough time to refactor the code, he or she should probably factor out the common parts, and then use proper (subtyping) inheritance. In view of this, I don't think making similarity overly complicated is a good idea.

> Sure, classical subclassing (...) remains an important use case. (...) even > without such verification the use case remains important.

It's precisely the lack of such verification that destroys the guarantees you can extract from Liskov's substitution principle.

<comment><USER15> > If the programmer has enough time to refactor the code, he or she should > probably factor out the common parts, and then use proper (subtyping) > inheritance.

I do see the attraction of the idea that all implementation sharing ultimately reflects something that can be expressed through a subtyping hierarchy adhering to the LSP, but I'm not 100% convinced that it is the case. Absent certainty on that point, I'd like to have a model of similarity that is workable when viewed as an ultimate, rather than interim, model.

Also, I think that this kind of explicitness (with similar syntax) is desirable for subtyping inheritance in languages that support multiple inheritance, so it keeps similarity in line with inheritance, with the distinction only in the relationship being expressed (so, while it may make similarity more complex, it keeps the whole language more simple but explicit.)

> It's precisely the lack of such verification that destroys the guarantees > you can extract from Liskov's substitution principle.

I agree that static verification of sanity in subtyping inheritance reduces the problem of LSP violations (as, frankly, does non-static verification, such as testing frameworks using subtype relationships to automatically apply tests for supertypes to subtypes.)

The degree and type of verification (as well as other details like cherrypicking and support for multiple similarity) that is appropriate for these type of things really depends on the language. Extending Perl or Python (dynamic languages with multiple inheritance) to support similarity alongside inheritance is probably going to favor different tradeoffs than extending Java (static with single inheritance) to support similarity.

<comment><USER5> > I agree that static verification of sanity in subtyping inheritance reduces > the problem of LSP violations

The LSP itself is never violated in a type-safe language. What's violated is the guarantees that you expected the LSP to buy you. For instance, Java doesn't promise about the meaning of non-final methods. If a programmer assumes such guarantees, the problem is with the programmer, not Java. If he wants such guarantees to hold, he has to use a different language.

</comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment><comment><USER16>
        1 + eval(read_from_the_network())

> If we get an integer, that expression is fine; if we get a string, it's not. > We can't know what we'll get until we actually run, so we can't statically > analyze the type.

> The unsatisfying solution used in practice is to give eval() the type Any, > which is like Object in some OO languages or interface {} in Go: it's the > type that can have any value. Values of type Any aren't constrained in any > way, so this effectively removes the type system's ability to help us with > code involving eval. Languages with both eval and a type system have to > abandon type safety whenever eval is used.

No, you can give EVAL a returning type of any (T) but still make type inference work.

        (+ 1 (eval (read)))

The meaning of a type in SBCL is: _if an expression evaluates without error, then its type is ..._. In other words, the type of the above expression is NUMBER.

There is also a NIL type (bottom) which represent the empty set of values. If a function has a return type of NIL, it means that it does not return a value normally. This is the case for the ERROR function, or the (LOOP) expression.

<comment><USER17> > if an expression evaluates without error, then its type is…

What's curious is that this is true in every language with general recursion/looping:

        int f(int n) {
            while (true) {}
            return n + 1;
        }

(In other words, the type of "f" isn't the logical formula "a → a", but rather "a → ¬¬a".)

The type of "eval" can also be fully static; it can just return a type that you have to do case analysis or runtime type inspection on to use.

In Haskell, with a data type:

        data Value = Integer Int | Text String | ...

        eval :: String -> Value

        foo = do
          value <- read_from_the_network
          let result = eval value
          case result of
            Integer n -> return (1 + n)
            _ -> error "that was not an integer!"

Or with RTTI:

        eval :: String -> Dynamic

        foo = do
          value <- read_from_the_network
          let result = eval value
          case fromDynamic result of
            Just n -> return (1 + n)
            Nothing -> error "that was not an integer!"

"fromDynamic" has the type:

        Typeable a => Dynamic -> Maybe a

Which is to say "given a dynamic value, and its runtime type information, I can either give you the value back with a static type, or nothing if I can't perform the downcast"—much like "dynamic_cast" on pointer types in C++.

<comment><USER16> > What's curious is that this is true in every language with general > recursion/looping:

Yes, thanks for pointing that out.

> The type of "eval" can also be fully static; it can just return a type that > you have to do case analysis or runtime type inspection on to use.

I'd say that's what type T stands for in Lisp, since you can then dynamically dispatch on the actual type of the value.

</comment></comment></comment><comment><USER18>
        add : (x : Nat) -> (y : Nat) -> {auto smaller : LT x y} -> Nat
        add x y = x + y

Actually, (GHC) Haskell can express something like this type if you turn on the required extensions.

        data Nat = Z | S Z deriving (Eq, Ord, Show)
        data SNat (n :: Nat) where
            SZ :: SNat Z
            SS :: SNat n -> SNat (S n)

        type family Compare (a :: Nat) (b :: Nat) :: Ordering where
          Compare Z Z = EQ
          Compare (S a) Z = GT
          Compare Z (S a) = LT
          Compare (S a) (S b) = Compare a b

        type a < b = Compare a b ~ LT

        type family (+) (a :: Nat) (b :: Nat) :: Nat where
          Z + a = a
          a + Z = a
          (S a) + b = S (a + b)

        add :: (a < b) => SNat a -> SNat b -> SNat (a+b)
        add = ...

</comment><comment><USER19> Being someone who enjoys C (Though I readily admit the type system is generally weak compared to the others on the list) there's a _little_ misinformation on this page that I think it worth clearing up - though I think the majority of the information is still perfectly good:

C does not 'allow' you to do a lot of the things mentioned on this page, it just doesn't generally carry around all the information to check and make sure you don't. So the page is generally still right - it can be fairly easy to get memory-unsafe things past the compiler - though whether you want to blame the type-system or blame the compiler could be debated, probably a bit of both.

Reading past the ends of arrays is illegal, as is accessing a variable of one type through a pointer to another type. Some of these things _will_ get checked or assumed by the compiler (And thus break code that does these things, and in some ways break them in subtle ways), but there's no guarantee that the compiler actually knows you broke the rules. This tends to be the main different from other languages like Java, where the compiler/run-time _does_ carry around such information and thus can do such checks and does know you when you broke the rules.

With that said, while there are some things I don't like about the design of Rust overall, giving the compiler more information to be able to figure out the above is definitely one thing they did right in comparison to C. The problem C is approaching is that, even though compilers are getting smarter, enough code was already written before that was the case to result in breaking when you attempt to use 'smarter' compilers.

<comment><USER12> Since this articles is about types, any behaviour the compiler doesn't check or doesn't generate a type error is "allowed".

<comment><USER19> The issue with that idea is that there is more than one C compiler. Some of them issue no warnings at all, and some of them (like `gcc`) will yell at you and probably break your code if you attempt to do some of the things I outlined.

I disagree with saying it is 'allowed' by the type system, because a smart C compiler could throw out your code and refuse to compile it if you do such things, and in doing so would still be within the C standard. Some work has already been done on this front in `gcc` (And I would assume `clang`) - <USER2> issue tends to be that by doing this, you end-up breaking some older code that used to work fine. And since older compilers didn't enforce these rules, people aren't as aware of them.

<comment><USER20> In theory, languages have type systems. In practice, compilers do.

If there's more than one C compiler, and they behave differently on this, then in practice, C has more than one type system.

</comment></comment></comment></comment><comment><USER21> Someone who has experience with both Agda and Idris, could you comment on which is easier to get started with? Coming from a Haskell background, I'd like to try my hand at writing more interesting constraints into my types. Any experiences using these languages for (non-research) work?

I've played around with Coq a little and it certainly feels more like a proof assistant than a programming language. It was fairly confusing and unfamiliar, but interesting nonetheless. Something with slightly more traditional ergonomics might be easier to pick up.

<comment><USER22> I found Idris pretty easy to get started with - that's part of it's goal, making dependent types easier to get started with. From there I think it would be easier to go to Agda or Coq, but I haven't explored much there myself.

<comment><USER23> +1 for Idris. The tutorials were pretty solid for it too when I tried it, and the mailing list was very helpful when I had questions [1]. There's even a WIP book! [2]

[1] [https://groups.google.com/forum/#!forum/idris- lang](https://groups.google.com/forum/#!forum/idris-lang)

[2] [https://www.manning.com/books/type-driven-development- with-i...](https://www.manning.com/books/type-driven-development-with-idris)

<comment><USER22> Their main docs are pretty good too: [http://docs.idris- lang.org](http://docs.idris-lang.org)

</comment></comment></comment></comment><comment><USER24> > If we try to call this function as add 2 1, where the first argument is > larger than the second, then the compiler will reject the program at compile > time

This is not entirely true in general. auto is only a best bet. It might not be able to prove something right and will fail. For example, if it has to recurse deeper than 100 constructors. In this case, the user of the function has to provide a proof himself.

See it as a bloom filter. if it doesn't fail, you're sure your function is correctly called. if it does fail, it might still be correctly called but the compiler just didn't have enough time to prove it. In that case a user needs to convince the compiler that he is right.

<comment><USER25> I don't see the problem here. I said that Idris will reject the program if the proof fails. That's what you're saying too, unless I'm very confused. (It's late, so maybe I am?)

<comment><USER24> Yes, but it might also reject correct programs. that's the point I'm trying to make.

<comment><USER25> That doesn't contradict the quote that you provided. The quote just says "the compiler will reject the program at compile time [if there's no valid proof]", which is true.

</comment></comment></comment></comment><comment> one-more-minute If this topic interests you, Julia's type system is worth checking out [1]. Julia is unusual among dynamic languages not just for having a powerful type system (including parametric types), but also for making it idiomatic to make full use of types. Aside from the benefits to the programmer, this also allows the compiler to get to Rust-level performance where it's needed.

I find that this is a nice compromise that gets a lot of the benefits of static type systems (small, local errors as opposed to behavioural bugs, ability to express concepts and constraints) without the drawbacks (loss of interactivity, or inability to escape the type system when it's appropriate).

[1]: [http://docs.julialang.org/en/latest/manual/types/](http://docs.julialang.org/en/latest/manual/types/)

<comment><USER26> It sounds very ad-hoc? (And Julia's benchmarking has not been reputable in the past, FWIW).

> without the drawbacks (loss of interactivity, or inability to escape the > type system when it's appropriate).

One can absolutely have interactivity in a statically typed language, and virtually all statically typed languages support casting when you absolutely need to.

<comment> one-more-minute "Ad-hoc" seems subjective but to me it feels very carefully thought out. And while I've found the benchmarks to be accurate, I more importantly mean myself (and many others I know) being easily able to get high performance code when we need it.

My second paragraph was only a personal take on what works for me, so YMMV, but nevertheless: I'm not arguing against static type systems in principle, but these are practical concerns rather than theoretical ones. I don't know of a mainstream static language that supports a fully dynamic REPL (Haskell's isn't great) and casting back and forth to `interface{}`, `Object`, or `Any` everywhere is a huge overhead compared to duck typing. I look forward to a future language solving those issues, but that's not the case today.

<comment><USER26> > "Ad-hoc" seems subjective but to me it feels very carefully thought out.

I guess I'd like to see a simple formalism.

> myself (and many others I know) being easily able to get high performance > code when we need it.

That's true for a lot of languages. It doesn't make it Rust-level.

> I'm not arguing against static type systems in principle, but these are > practical concerns rather than theoretical ones. I don't know of a > mainstream static language that supports a fully dynamic REPL (Haskell's > isn't great) and casting back and forth to `interface{}`, `Object`, or `Any` > everywhere is a huge overhead compared to duck typing. I look forward to a > future language solving those issues, but that's not the case today.

Shrug, not my experience. I don't know what you mean by "fully generic", but I've been very happy with the Scala REPL - I've never once thought "this would be easy in the Python REPL but I can't do it because of the type system". Casting is indeed a big overhead if you're doing it everywhere, but my experience is the cases where you need it are extremely rare.

</comment></comment></comment></comment><comment><USER27> > The unsatisfying solution used in practice is to give eval() the type Any, > which is like Object in some OO languages or interface {} in Go: it's the > type that can have any value. Values of type Any aren't constrained in any > way, so this effectively removes the type system's ability to help us with > code involving eval. Languages with both eval and a type system have to > abandon type safety whenever eval is used.

It's not quite true that they must abandon type safety: rather, they must abandon a certain kind of compile-time type check. It's not _unsafe_ to use eval or Any values (i.e., it won't crash the system); one must simply examine the Any value (at runtime, natch) and do stuff with it.

</comment><comment><USER28> > Haskell has no equivalent of the Idris type above, and Go has no equivalent > of either the Idris type or the Haskell type. As a result, Idris can prevent > many bugs that Haskell can't, and Haskell can prevent many bugs that Go > can't. In both cases, we need additional type system features, which make > the language more complex.

Type checking Idris is much simpler than Haskell.

<comment><USER29> > Type checking Idris is much simpler than Haskell.

Is it? How's that?

<comment><USER28> Haskell's type system has accrued complexity over the years, whereas a small dependently typed language can be implemented very easily [1].

[1] [https://www.andres-loeh.de/LambdaPi/](https://www.andres- loeh.de/LambdaPi/)

<comment><USER20> That's not a fair comparison, though. A fair comparison might be Haskell's type system compared to Idris, or a small dependent typed language against a small type inference language.

<comment><USER28> The question was, "how's that?". My answer was, it's possible for a small dependently typed language to be simpler than Haskell.

I'd already made the assertion that Idris was less complicated than Haskell.

</comment></comment></comment></comment></comment><comment><USER30> I've been very curious about Perl6's gradual typing[1] which, as I understand it, draws a lot from Haskell's type system but in an optional manner.

        subset NonNegativeInt of Int where * >= 0;

        sub fib(NonNegativeInt $nth) {
          given $nth {
            when 0  { 0 }
            when 1  { 1 }
            default { fib($nth-1) + fib($nth-2) }
          }
        }

[1] [http://blogs.perl.org/users/ovid/2015/02/avoid-a-common- soft...](http://blogs.perl.org/users/ovid/2015/02/avoid-a-common-software-bug- by-using-perl-6.html)

</comment><comment><USER31> Kind of shocked nobody mentioned this, even if it is a bit of an aside, but umm, I've been dying for anything at all from Gary Bernhardt - I don't even know what to say except that it makes me hope however unrealistically that will one day get something like the magnum opus that is "Destroy All Software" from him again.

<comment><USER32> You know he just started to post new stuff, right?

[https://www.destroyallsoftware.com/screencasts/catalog](https://www.destroyallsoftware.com/screencasts/catalog)

<comment><USER31> I know. Mind blown.

</comment></comment></comment><comment><USER33> The article is mainly focused on static typing. I guess it makes sense since the title is "Types". Although reading this, you might just think that dynamic typing is good for nothing.

The more practical part of the article is great. However, the theoretical part could use some works. Especially the terminology isn't actually clear.

Takes some example, what is "memory-safe" ? The only example makes it sounds like bound checking. And what does "no way to escape the language's type rules" mean? There are some usages of "valid program" and "invalid program" that makes my spider sense tingling as well.

> However, no dynamic language can match the speed of carefully written static > code in a language like Rust.

> Any blanket statement of the form "static languages are better at x than > dynamic languages" is almost certainly nonsense.

Those two quoted statements come from the article.

The section "Arguments for static and dynamic types" didn't mention a single reason why dynamic typing could be preferable over static-typing. It could at least have mentioned macro, or hand-waving claim that dynamic typing makes for prettier code at time (I think the author of python's requests library made that argument in one of the blog post).

I think the last part should be removed (ranking the language by their typed system power).

> There's a clear trend toward more powerful systems over time, especially > when judging by language popularity rather than languages simply existing.

Well, it looks true since we didn't consider any dynamic language at all...

Focusing more on the "Concrete examples of type system power differences" would have been better for the article, I think. Something along the line of an example of Non-nullable type with something simple in TS, to optional/maybe monad, then dependent type that you can implement matrix multiplication (m * n) with (n * p) and got the type (m * p) back.

<comment><USER34> > The author is highly probably on the static typing camp.

First and foremost, i don't think dividing people into these clear-cut "camps" is good for this discussion (and probably for most discussions too...).

Second, i very much doubt that the author is a purist of static typing, or even a strong proponent of it. He has a series of programming screencasts called Destroy All Software, and in most the episodes i've seen he uses Ruby, Bash or Python. So, unless he has dramatically changed his style since then, i don't think he has any problems using dynamically typed languages.

<comment><USER33> Sure, I will edit that sentence out.

</comment></comment></comment><comment><USER35> So what might make dynamic languages easier to write in? I enjoy dynamic languages more because:

* "double kilometerToMiles(double km) { return km / 1.6 }" here the types are of really low value, yet I have to type them in.

* a mutable list of generic and variable arity event handlers. Really hard to specify as a type (most languages cannot do it). Really easy to use.

* co- and contravariance and lists, the math works exactly opposite of intuition.

* when you change your mind on the types/arity beyond what your IDE can follow, it is a lot of low value work

* if your test suite has 100% coverage of arguments/return-value and field usage, you have type checked your program.

If I am going to write down types, they better be of value to me. Like types that are null or non-null, or tainted (from the user) or non-tainted. Or two numbers that are kilometers vs miles. Or only accessible under a certain lock or other preconditions. And be flow sensitive (kotlin style null checks), not make me write even more code.

But if you are a dynamic language, do be strong typed (no "1" == 1). Also be dynamic in arity, that is (part-of) a type. Don't be handwavy scoped, lexical scoping is probably the only predictable way to do scoping. Fail fast, like prevent spelling mistakes for non existing field names for example. And allow this to work: "2 * Vector(10,10)".

And from that perspective there is still quite some room for improvement in all classes of languages, I suppose, and good that there is lot of movement and experiments today.

<comment><USER36> Writing down the types isn't a necessary property of static type systems. In Haskell, F# or (OCa)ML, you almost never have to actually mention any types.

I do agree that Java-style types aren't of much value. That catches few errors at significant cost.

<comment><USER37> Catching logical errors is only one out of several things that static typing gives you.

Incidentally, right now this is a few positions above this posting: [https://news.ycombinator.com/item?id=12350063](https://news.ycombinator.com/item?id=12350063)

Better IDE support, a kind of self-documentation and increased performance come to mind. I know there are counter-examples for each of these points, but the general trend cannot be denied.

</comment></comment></comment><comment><USER38> I think types can be both frustrating and life-saving.

However, the "danger" for me lies primarily in implicit conversion. That is, Python vs Lua/JS/R. When writing large programs, some things happen implicitly and linting may not catch it.

Everything else besides implicit conversion just helps structure the programs in specific ways and helps speed them up.

</comment><comment><USER39> So, how do these advanced types work?

For their `add` example, I imagine just having x<y somewhere is sufficient. But let's say I write a sorting implementation but the compiler can't quite figure out that it is correct. Is there a provided way to tell the compiler 'trust me, this property is now true'?

Also, I'm assuming the compiler has two classes of errors here: a counter example vs. unable to prove?

Can anyone point me to documentation on these (in Idris, preferably)?

<comment><USER10> In Idris, there are functions `believe_me` and `really_believe_me`, which assert things to the compiler. They're generally only used for FFI code, or for extremely tedious proofs.

If you wanted to prove that a list was sorted, you'd write another datatype:

        IsSorted : Ord a => List a -> Type

which represents the proposition that the input list is sorted. You could encode this by requiring that any new element in the list is either the first element, or less than or equal to the previous element. Then, your sorting function could return a dependent pair:

        (l : List a ** IsSorted l)

which can be read as the list, accompanied by a proof that it is, in fact, sorted. Since your `IsSorted` type requires that the list is sorted, via construction, you won't be able to supply the output of the function unless your code is correct (or you use `believe_me`!).

The compiler doesn't generally come up with counterexamples for you; with this style, if the proof isn't complete, it won't compile.

</comment></comment><comment><USER40> I wrote a little article, which has been posted here before, that shows the power of different type systems and what kind of bugs you can expect to prevent: [http://kevinmahoney.co.uk/articles/tests-vs- types/](http://kevinmahoney.co.uk/articles/tests-vs-types/)

</comment><comment><USER41> What types are and what they do is all well and good, but what I want to know is how they would help me write a larger application. Would it help it be more correct, be written faster, or be more flexible? Does it just help on a line- by-line programming basis, or can it have any ramifications on a higher level like product design?

</comment><comment><USER42> > A type is a collection of possible values

Ok, but do two collections with the same values always correspond to the same type?

<comment><USER43> It depends on the language. If you have, for example, a type-and-effect system, a type can contain information (like "we wrote to stdout while computing this") that is never reflected in the value itself. In a formal setting, it's more appropriate to describe a type as a collection of pieces of source code, so that `2+3` and `print "computing...\n"; 2+3` have different types even though they'll evaluate to the same thing.

</comment></comment><comment><USER44> I read that type checking is easier with dependent types.

I'm barely aware of adga adn other langs that are used for profs, but wonder how useful (and what danger ahead) could cause to design a language with this for more mundane purposes (like, for example, build websites, data manipulation, gui, etc)

</comment><comment><USER45> Hope this makes it into Wikipedia instead of here.

<comment><USER46> It'll get moderated out.

<comment><USER33> For this article specifically, it shouldn't be on wiki. The quality still needs a lot of polish, and too much opinions.

<comment><USER25> This is a decent summary of why I'm doing it. Wikipedia is full of "high quality" content, where "high quality" means "directly exposing every detail that anyone ever thought to nitpick", and this topic is a nitpicker's paradise. There's value in that detail-heavy style, but it's difficult to learn from and it's a type of writing that I rarely want to read. As for opinions (and lack thereof), they prevent Wikipedia from saying many things that are both well-known and easily stated. The lack of clear summaries of widespread disagreeing opinions keeps our implicit knowledge implicit.

<comment><USER33> I agree with your reasoning and your description of Wikipedia.

However, despite all the flaws, wiki is reasonably helpful, and I doubt filling it with more opinions would be helping the case.</comment></comment></comment></comment></comment>


Chezscheme 9.5 in Debian Testing - <AUTHOR> https://packages.debian.org/buster/chezscheme9.5 <comment><USER2> You can easily install ChezScheme by git cloning and following these instructions on the github page: [https://github.com/cisco/ChezScheme/blob/master/BUILDING](https://github.com/cisco/ChezScheme/blob/master/BUILDING)

You do not have to wait for your package manager to catch up.

</comment><comment><USER3> So little dependencies... Awesome!

<comment><USER4> There is a reason for that. From the BUILDING file in the Chez repository:

> Since the Scheme sources can be compiled only by a working version of Chez > Scheme, it's not actually possible to build Chez Scheme from source. That's > why the boot and header files are packaged with the sources.

Chez has a bootstrapping problem. (See also [https://bootstrappable.org](https://bootstrappable.org))

<comment><USER5> What a strange site configuration. HTTPS is broken because the cert is valid for another hostname and www.bootstrappable.org and bootstrappable.org differ in content.

The site's intention is nice in theory but not really feasible in practice. To get an OS running on a computer, you need binaries anyways, so having some more binaries for programming languages isn't really that bad. There also isn't even an easily bootstrappable C compiler afaik.

<comment><USER6> "The site's intention is nice in theory but not really feasible in practice. "

They've gotten really far, though. Jan's Mes can compile tcc which can compile an older version of GCC. They just need enough of a C compiler to compile MesC.

[https://gitlab.com/janneke/mes](https://gitlab.com/janneke/mes)

"To get an OS running on a computer, you need binaries anyways, so having some more binaries for programming languages isn't really that bad. "

That's what I keep telling folks obsessed with this. If they insist on non- binary solutions, David A. Wheeler and I talked about using scripting languages that come with the system. I was looking at Perl with him mentioning bash. I added bash, Tcl, and awk compilers to the bootstrapping collection just in case. That's on top of the usual like Forth, LISP, or META II. People put some small, C compilers in there like you were talking about.

[https://bootstrapping.miraheze.org/wiki/Main_Page](https://bootstrapping.miraheze.org/wiki/Main_Page)

<comment><USER7> Actually you missed a bunch. MesC is soon going to be bootstrapped by M2-Planet [https://github.com/oriansj/M2-Planet](https://github.com/oriansj/M2-Planet) Which is in the process of becoming bootstrapped via stage0 [https://savannah.nongnu.org/projects/stage0/](https://savannah.nongnu.org/projects/stage0/) Which bootstraps off a sub 240Byte hex Monitor and is hardware neutral.

<comment><USER6> Appreciate the update!</comment></comment></comment></comment></comment></comment>


Technology Is Wiping Out Companies Faster than Ever - <AUTHOR> http://www.technologyreview.com/view/519226/technology-is-wiping-out-companies-faster-than-ever <comment><USER2> Is technology wiping out companies? The data shows that companies on the S&P 500 are averaging shorter lives, but nothing is shown to suggest it's technology at fault. One could propose it's modern management practices, or global competition, or some particular demographic effect having to do with the startup rate of new companies.</comment>


Show HN: Gandul – accessible image lazy loading - <AUTHOR> https://github.com/alterebro/accessible-image-lazy-load <comment> fake-name Or maybe, and this might sound crazy, just USE THE FUCKING IMAGE TAG.

Seriously, websites where the images are broken without javascript are FUCKING ANNOYING.

There are image formats that support progressive rendering. Use those if you're that desperate, but STOP BREAKING THE FUCKING INTERNET FOR NO GOOD REASON.

<comment><USER2> Native lazy-loading[0] is coming in Chrome 75.

<img src="celebration.jpg" loading="lazy" alt="..." />

[0] [https://addyosmani.com/blog/lazy- loading/](https://addyosmani.com/blog/lazy-loading/)

<comment><USER3> With some caveats, like _" An implementation detail of loading in Chrome is that it fetches the first 2KB of images on page-load."_.

That's the first 2KB of every image.

<comment><USER4> So on the server side I will have lots of errors/exceptions because only 2kb were loaded of each image?

<comment><USER3> I assume it does a range request. I was more pointing out that the client will still make a lot of requests. So maybe still not a good approach for long pages with tons of images below the fold.

</comment></comment></comment></comment></comment><comment><USER5> Rendering an anchor in the place of an image in case I don't have JavaScript enabled doesn't feel very good.

I'd much rather have image solutions that fail gracefully if the JS on my page is broken or the user just has it disabled.

<comment><USER6> Why not? I think it is a fair replacement for a lot of image use-cases. If JS is turned off, users still have the ability to view the image and description, without the image hurting your total page weight.

</comment></comment><comment><USER7> How about lazily turn <a> into <img> without hurting performance ? It's useful if the network is slow.

</comment><comment><USER8> I don't get why this is supposed to be particular accessible. <img> is perfectly fine, the alt attribute should describe the contents for users with screen readers and crawlers.

</comment><comment><USER9> I really dig this approach. Pretty clever.

</comment><comment><USER10> Nice name!</comment>


Neo4j introduces new cloud service to simplify building a graph database - sahin-boydas https://techcrunch.com/2019/11/06/neo4j-introduces-new-cloud-service-to-simplify-building-a-graph-database/Neo4j <comment><AUTHOR> This links to the wrong article. It should be [https://techcrunch.com/2019/11/06/neo4j-introduces-new- cloud...](https://techcrunch.com/2019/11/06/neo4j-introduces-new-cloud- service-to-simplify-building-a-graph-database/)</comment>


Please don't let yourself get Stereotyped - <AUTHOR> http://paraschopra.com/blog/personal/stereotype.htm

<comment><USER2> Nothing matters, therefore everything matters. Good piece.</comment>


The Real Problem with Facebook - <AUTHOR> http://diegobasch.com/the-real-problem-with-facebook

<comment><USER2> Well, that and there's nothing that really provides places where new relationships can form casually. FB is a tangle of little fiefdoms, with little opportunity to expand your personal circle except through existing connections. This is quite limiting.

The polar opposite is something like HN or Reddit, which is all casual contact with random people, albeit those who are likely to share our interests. This allows for often more fulfilling discussion than what our immediate circle of friends can provide. It's why we're here rather than on FB or G+ right now. We humans have always liked marketplaces and pubs in addition to private venues.

</comment><comment><USER3>
      "So far, Facebook has defaulted to the laziest of all revenue models:
      ads. It is clear that Google is the king of ads,
      and this is because intention beats behavior."

A well-turned phrase, but actually Facebook has intention in a way far more nuanced than what Google captures. When I "like" something on Facebook not only am I sharing my intention, but a very focused one that _directly reflects my interests_. I may search Google for things not directly relevant to me (homework, curiosity) I may buy or look for things on Amazon that are not for me (gifts, price comparisons) but if you like something on Facebook chances are that Facebook can strongly infer what other things you may like--Netflix has in fact profited off of this exactly (as has Amazon) and it is possible to build a recommendation engine from this data that will have greater potential than Google's because it can predict what ads will be likely to resonate with customers.

</comment><comment><USER4> The money is in display ads. It's not about intentions only.

You don't buy a can of coke online. But coke definitely want to advertise online and be able to target very specific crowd based on an FB profile.

</comment><comment><USER5> I would not be surprised if FB is positioning itself to be the "front page of the internet". Many people spend lots of hours on Facebook and most of the time FB serves as their portal to current events, news, events, etc. Search is definitely missing, but I am sure that is in the works. FB is still a relatively young company and it's exciting to see where they are headed, especially now that they have plenty of cash to invest in the business.

Of course, this is all just conjecture at this point.

<comment><USER6> You know who else tried to be the 'front page of the Internet?'

Yahoo Lycos AOL MSN

It's not going to happen. There is little to no value in being the front page of the Internet. People are very good at ignoring the screen when they're typing their search query in the address bar.

</comment></comment><comment><USER7> "Google has been trying to diversify their business and create another significant revenue stream for years. No dice so far."

I look forward to seeing the next full quarterly results that include the new Google Drive & additional storage plans. It was such a big price increase from the old plans I would think there will be an impact even if sign up #s of paid plans were low.

</comment><comment><USER8> History repeating itself, Facebook will go the way of AOL and Yahoo. The real winners are the future entrepreneurs who will receive funding from the thousand odd millionaires this has made

</comment><comment><USER9> "the big bet is that some of those users. . . will some day pay a lot of money to Facebook. For what?"

Surprised to see zero mention of "credits" in the article.

Credits was $557-million in 2011 and expected to grow 4x this year.

That alone isn't going to justify the valuation, but still odd to leave out of the discussion.

ref - [http://techcrunch.com/2012/04/22/40-billion-facebook- credits...](http://techcrunch.com/2012/04/22/40-billion-facebook-credits/)

<comment><USER10> There is also the consideration that they could extend their offerings. People spend a long time on Facebook, especially when compared with the amount of time they spend on Google. If Facebook wanted to extend into search, or business applications, or anything online really, they are working with a base of close to a billion customers. That's a billion users who they have the opportunity to introduce a new service to.

Any potential new service could provide a more effective platform for ads.

<comment><USER11> That line of argument is much more effective for young companies. FB has plenty of cash to say buy duck duck go, but they are still focused on social and making less than 1$/user/month.

<comment><USER12> Why? They can change their focus. With all that money and talent they can build and buy almost anything.

</comment></comment></comment></comment><comment><USER13> And the counter argument is television.

<comment><USER14> They can't preempt their content with ads like television. That'd be the signal for half their users to leave and the stock to tank. The user don't view Facebook as entertainment, so they won't be willing to sit through ads.

More likely, the distinction between content and advertisement will erode until you can't tell the difference anymore, e.g., product placement.</comment></comment>


15 Exercises for Learning a new Programming Language - <AUTHOR> http://www.freelancingjob.com/articles/article_description.php?art_id=96 <comment><USER2> These are awesome. They cover all the basics a person might need to know in a new language and they're not one's basic "find out if a number's prime" ones that I'm used.

Though I couldn't agree with the author's comment in the intro that program 0 should be "Hello, World!". Just for the sake of going through the motions of actually coding a thing (app or otherwise) in the new language.

<comment><USER3> Indeed, a single program that ran through all of these in sequence could be a proper (or better) substitute _for_ a reference card.

<comment><USER2> An excellent point.</comment></comment></comment>