Nikon Issues Small Recall for a 16-Year-Old Film Camera - <AUTHOR> https://petapixel.com/2020/07/16/nikon-issues-recall-for-16-year-old-film-camera-due-to-european-law/ <comment><USER2> I love how the idea of Japanese conscientiousness has become almost a meme for how we wish companies were run. Other examples you've probably seen come across the webs:

\-- Hotel apologizes in advance for a brief 1 minute planned disruption of in- room internet at 4am [https://www.google.com/search?q=japanese+hotel+internet+apol...](https://www.google.com/search?q=japanese+hotel+internet+apology&source=lnms&tbm=isch&sa=X&ved=2ahUKEwiR0fyo19LqAhVZHjQIHWLwBWkQ_AUoA3oECAsQBQ&biw=1751&bih=1211#imgrc=cZGXwwqZeLmVcM)

\-- Japanese train company issues apology for train leaving 20 seconds early [https://www.google.com/search?q=tokyo+rail+apologizes+train+...](https://www.google.com/search?q=tokyo+rail+apologizes+train+leaving+early&oq=tokyo+rail+apologizes+train+leaving+early&aqs=chrome..69i57.6899j0j7&sourceid=chrome&ie=UTF-8)

<comment><USER3> To be honest, a train leaving early SUCKS. You show up on time, and you find out you missed it. Maybe the next one is in 40 minutes, maybe it's the next day.

(I realize 20 seconds early isn't _that_ bad, but still, leaving early by any amount of time is a lot worse than leaving late)

<comment><USER4> In this case, it's annoying because you'll have missed a semi-rapid and the next comparable train to Tsukuba will leave 9 minutes later and arrive 17 minutes later.

It's not a 40 minute thing, this isn't Caltrain, but it's still annoying.

<comment><USER2> I loved (not really) how sometimes on Caltrain, if the trains were delayed, you could get on a delayed train after you had planned to travel, and get home sooner than if the trains had been running on time.

(I'll let you think on that puzzle.)

<comment><USER5> The same often happened when I travelled by train in Ontario (Canada) where the trains shared the line with freight service, and so sometimes a train was poorly timed such that it always had to wait for freight to clear first. In those cases, if a train was delayed, the delay occurred at the station such that by the time the train left the station, the freight had already cleared and the time spent on the train was less than it normally would be. Also, delays often meant the train had fewer passengers, so it would stop for less time at each stop... am I on the right track? ;-)

<comment><USER6> The freight reason is not it. If the freight got out of the way at the same time it normally would, you would decrease your travel time, but arrive at your destination no earlier than a normal arrival.

The second reason is closer, but not exact. Caltrain has a variety of different types of trains, some stop at all the stops, some stop at only a few. Here's the scenario: there is a fast train then a slow train. Your normal schedule doesn't allow you to get to the station on time to take the fast train, so you normally take the slow train. If both trains are behind schedule, you might be able to make it to the fast train, and since it goes faster you might get to your destination earlier than normal.

<comment><USER2> Yes.

The frustrating thing of course, is that even if the trains are severely delayed, they have (or had, depending on whether they go out of business soon) a policy of running the exact schedule for each train as if normal. So even if all the trains are piled up, delayed, they would make local stops, etc. even though everyone wanted just to get to the end by express.

Sad that this kind of issue was so common (1x per month at least) that I blame them for not having efficient accident backup plans.

Yes, yes, I realize that there's the problem of having trains go express when they're all stacked up.

</comment></comment></comment></comment></comment></comment></comment><comment><USER7> Nikon still sells the Nikon F6 film camera. All existing stock was probably manufactured a loooong time ago, but it is still technically a current product, so I imagine they're obligated to provide support and government- mandated environmental fixes.

[https://imaging.nikon.com/lineup/filmcamera/slr/f6/](https://imaging.nikon.com/lineup/filmcamera/slr/f6/)

(I'd love to see the sales numbers for the F6. I can't even guess the order of magnitude of global sales per year. 1? 10? 100?)

<comment><USER8> I still occasionally use a now over 40 years old Nikon FE that, despite having never been serviced as far as I can tell, still works flawlessly. It even does some things better than even the most modern DSLRs, it can, for example automatically expose exposures several minutes long correctly (far outside spec, but works). Impeccable quality. The old Ai-S lenses, too, entirely made from glass, brass, aluminium and some steel with a touch of cement, it pleases the gods. Zeiss still makes them like they used to.

<comment><USER7> Which is why I'm always surprised the F6, to borrow a phrase from Apple, "remains a product in our lineup".

There's so many used and seemingly indestructible film cameras floating around. I guess there's some market for a factory-warranty film SLR, but I'm not sure what said market is. If I was going to shoot film, I'd find a Nikon F5, which would also double as a medieval flail for self-defense.

(Or I'd pull my 1983 Pentax off the shelf.)

<comment><USER9> The F6 is the most advanced and capable film DSLR ever built. If you want/need to shoot film and you shoot subjects that are commonly done with modern digital SLRs and considered out of reach of a film camera, the F6 is the best game in town. The fastest and lowest-light-capable autofocus, the best matrix metering, digital recording of exposure data (just like EXIF but for film!), etc. It is a modern pro camera that happens to shoot film.

Most people don't need it but it's certainly unique. If you do need it, there is no substitute.

Nikon actually has a history of doing that as well. The Nikonos series underwater cameras were really the only thing in their class, with unique water-contact optics that avoided rainbow diffraction from the port by putting the optics right against the water. They also made unique 180-degree orthographic lenses for atmospheric surveying - measure cloud cover/etc by photographing the sky every day and get the full horizon to horizon in one frame. etc etc. They really are a fascinating company.

Check out the 1001 Nights of Nikkor, a fascinating series of stories about all that stuff.

[https://imaging.nikon.com/history/story/](https://imaging.nikon.com/history/story/)

<comment><USER10> > digital recording of exposure data (just like EXIF but for film!

Wait; how does that work ? Where is the camera writing the info and how do you retrieve it ?

<comment><USER9> It has an internal memory bank and you dump it to a compactflash card with the MV-1 accessory that the other person mentioned.

Personally I think that's a little clunky and it would be better to go with a little transflash card, but the F6 was designed in 2004 and I guess at that point it would have been a SD card and maybe even compactflash and they didn't want the size.

This approach is probably still preferable to direct USB connection though, because presumably that would require utility software that would now be incredibly out of date and tied to like Windows XP or something. If nobody bothered to write an open-source utility then that function would be unusable for modern PCs.

That's a problem on some hardware, I have a scanner where the only software that supports it is tied to Windows XP, or you can use third-party software (VueScan)_that talks to it directly and bypasses the official drivers. It is a scanner designed to do 4x5 film directly (not a flatbed) so replacements are thousands of dollars.

</comment></comment></comment></comment></comment></comment><comment><USER11> What really impresses here is their ability to document the recall to the point that a product that old still has its full history available, allowing them to work out which specific cameras to recall.

<comment><USER12> We somehow got used to the idea that we can expect companies not to do this because it's not profitable for them

</comment></comment><comment><USER13> I've never worked in a context that made complex physical goods. Is it surprising to others that they even know which specific 152 cameras were over- plasticized?

<comment><USER14> The f6 is high end low volume product. They only make and sell a couple hundred a year.

<comment><USER15> Looks like it costs $2,549. Which doesn't really seem profitable in volumes that low.

<comment><USER16> It's quite mechanically and electronically simple, though, by modern standards.

<comment><USER17> And some of the more complicated or precision components (shutter, mirror, viewfinder screen + prism, lens mount, autofocus motor, autofocus and autoexposure sensors) exist in similar forms on newer products and may be able to share production lines to some extent.

</comment></comment></comment></comment></comment><comment><USER18> Meanwhile Pentax is still avoiding the issue of multiple models dying from cheap apature solonoids.

<comment><USER19> I found out about that issue the hard way last week. Pictures started coming out too dark, and searching online quickly revealed that it's a well-known issue and that a class action lawsuit was filed back in May. Repairing it was pretty straightforward, but sourcing a replacement part from before production moved from Japan to China was unpleasantly expensive.

</comment></comment><comment><USER20> Meanwhile, my 2 year old mobile device...

</comment><comment><USER21> > In fact, only 152 units are impacted… so few that Nikon actually lists every > affected serial number in the recall notice:

</comment><comment><USER22> [deleted]

<comment><USER7> Yes, I'm sure the viral marketing for…a film SLR…that was introduced in 2004…is absolutely priceless.

<comment><USER23> TBF the benefit is to their entire business.</comment></comment></comment>


Hitless TLS Certificate Rotation in Go - <AUTHOR> https://diogomonica.com/2017/01/11/hitless-tls-certificate-rotation-in-go/ <comment><USER2> This is a good write up, and it's awesome to see on-line rotation of certificates.

But (there was always a but coming) ... the word "rotation" is over-used here and very dangerous, because it doesn't emphasize what's important. To many it means "deploying a new credential". That's not that important at all, at best it's a means to an end at worst it's make-work. What's important is that credentials are revoked. It's exactly like the important part of backup systems being that we can restore (and we should really call them "restore" systems).

When a credential becomes compromised, what you want to do is revoke it and make sure it stays revoked, other wise the attacker's goal is complete. So think of it a "Revocation" system, and call it that.

Viewed in that context, it become more apparent that the write-up doesn't mention, or test or check, that the credential actually is revoked and doesn't work any more. But that's the most critical step. Even if you're relying only on expiration times (which seems unsafe!) it's important to check for broken checks (like fail-open configurations that let everything in), broken clocks, etc ...

<comment><USER3> I think the more common case than a revocation is replacing an expiring certificate. I don't have hard data. It sure seems to me that short-lived certificates tend to rotate out far more often than they need to be revoked.

<comment><USER4> > It sure seems to me that short-lived certificates tend to rotate out far > more often than they need to be revoked.

For large majority of companies, would they even spot that their keys have been stolen? That's a few steps before revocation itself.

</comment></comment></comment><comment><USER5> Nice writeup!

I wish there was a CA out there that could let you requests new certs more frequently.

Yes there's Let's Encrypt, which is amazing and works great but the ratelimits[1] really kill you if you're not careful. I've had a few issues where I've triggered the LE ratelimit with a production domain and got locked out of making new certs for a whole week. I would gladly pay for an ACME CA which does not enforce these ratelimits.

[1] [https://letsencrypt.org/docs/rate- limits/](https://letsencrypt.org/docs/rate-limits/)

<comment><USER6> I'm actively considering what it would take to set up a for-profit ACME CA, and pricing based on rate limits might be the key business model insight I needed. Thanks!

<comment><USER7> LE needs $2MM/year to run and bootstrapped under an existing CA, so there's your starting point ;)

It might be easier to resell someone else's certificates.

</comment></comment></comment><comment><USER8> [https://github.com/square/ghostunnel/](https://github.com/square/ghostunnel/) is written in Go and does hitless cert reloading too.

</comment><comment><USER9> So I'm not 100% certain on this, but this flow seems like like it would be a good candidate for atomic.Value[0]? The the mutexes could be removed entirely. That way you don't need to get a lock on every config read.

[0] [https://golang.org/pkg/sync/atomic/#example_Value_config](https://golang.org/pkg/sync/atomic/#example_Value_config)

<comment><AUTHOR> Author here, I actually added a footnote exactly because of that fact: [https://diogomonica.com/2017/01/11/hitless-tls- certificate-r...](https://diogomonica.com/2017/01/11/hitless-tls-certificate- rotation-in-go/#fn:3)</comment></comment>


Making Rust as Fast as Go - <AUTHOR> https://www.christianfscott.com/making-rust-as-fast-as-go/ <comment><USER2> Note that Rust and Go programs differ in a seemingly insignificant, but actually important detail.

Rust does this

        next_dist = std::cmp::min(
            dist_if_substitute,
            std::cmp::min(dist_if_insert, dist_if_delete),
        );

Go does this

        nextDist = min(
             distIfDelete,
             min(distIfInsert, distIfSubstitute)
        )

The order of minimums is important for this dynamic programming loop. If I change Rust version to take minimums in the same order (swapping substitute and delete), runtime drops from 1.878696288 to 1.579639363.

I haven't investigated this, but I would guess that this is the same effect I've observed in

* [https://matklad.github.io/2017/03/12/min-of-three.html](https://matklad.github.io/2017/03/12/min-of-three.html)

* [https://matklad.github.io/2017/03/18/min-of-three-part-2.htm...](https://matklad.github.io/2017/03/18/min-of-three-part-2.html)

(reposting my comment from reddit, as it's a rather unexpected observation)

<comment><USER3> Since min(a, min(b,c)) == min(b, min(a,c)), perhaps the compiler should be smart enough to swap the comparisons around if it makes it quicker?

<comment> dan-robertson I suspect that statement is not true for floats. Possibly you don't get the same float from min(0,-0) as min(-0,0), and similarly with NaNs. Rust specifies that if one input is NaN then the other is returned but doesn't say what happens if both are NaN.

<comment><USER4> > Rust specifies that if one input is NaN then the other is returned but > doesn't say what happens if both are NaN.

It does. If both are NaN a NaN is returned. Note, however, that when Rust says that a NaN is returned, this means that any NaN can be returned. So if you have min(NaN0, NaN0) the result isn't necessarily NaN0, it might be another NaN with a different payload.

<comment> dan-robertson Right. That's what I was getting at but I didn't know the NaN-return rule.

</comment></comment></comment></comment></comment><comment><AUTHOR> Hey all, as some keen-eyed commenters have pointed out, it looks like the rust program is not actually equivalent to the go program. The go program parses the string once, while the rust program parses it repeatedly inside every loop. It's quite late in Sydney as I write this so I'm not up for a fix right now, but this post is probably Fake News. The perf gains from jemalloc are real, but it's probably not the allocators fault. I've updated the post with this message as well.

The one-two combo of 1) better performance on linux & 2) jemalloc seeming to fix the issue lured me into believing that the allocator was to blame. I'm not sure what the lesson here is – perhaps more proof of Cunningham's law? [https://en.wikipedia.org/wiki/Ward_Cunningham#Cunningham's_L...](https://en.wikipedia.org/wiki/Ward_Cunningham#Cunningham's_Law)

<comment><USER5> Thanks for following up. Just as an FYI, there's a few bugs in your implementation, the most obvious one is the use of ".len()" in a number of places interspersed with ".chars().count()". These two return different values. ".len()" returns then number of UTF-8 bytes in the input string, which for ASCII is the same as ".chars().count()" obviously, but if you do attempt any Unicode characters, your function won't work. ".chars()" provides Unicode Scalar Values (USVs) -- which is a subset of code points, excluding surrogate pairs [1]. Note also this is _not_ the same as a Go rune, which is a code point _including_ surrogate pairs.

Secondly, you re-implemented "std::cmp::min" at the bottom of the file, and I'm not sure if the stdlib version is more optimized.

Lastly, well, you caught the issue with repeated passes over the string.

I've fixed the issues if you're curious: [https://gist.github.com/martinmroz/2ff91041416eeff1b81f624ea...](https://gist.github.com/martinmroz/2ff91041416eeff1b81f624ea585f83a)

Unrelated, I hate the term "fake news" as it's an intentional attempt to destroy the world public's faith in news media. It's a cancer on civilized society. Somewhere your civics teacher is crying into some whiskey, even though of course you're joking.

[1] [http://www.unicode.org/glossary/#unicode_scalar_value](http://www.unicode.org/glossary/#unicode_scalar_value)

<comment><USER5> This doesn't even begin to get into the question of what Levenshtein Distance even means in a Unicode context. What's the Levenshtein Distance of 3 emoji flags? I suppose we should be segmenting by grapheme clusters and utilizing a consistent normalization form when comparing, but Rust has no native support for processing grapheme clusters -- or for normalizations I believe. The UnicodeSegementation crate might help.

Based on some cursory research, the go version differs in a more subtle way too. A Rune is a Code Point, which is a superset of the Rust "char" type; it includes surrogate pairs.

<comment><USER6> Levenstein (edit) distance is fundamentally an information-theoretical concept defined on bitstreams, as insertions/deletions/swaps _of individual bits_ within a stream. It has a lot in common with error-correcting codes, fountain codes, and compression, which all also operate on bitstreams.

Any higher-level abstract mention of Levenstein distances (e.g. of Unicode codepoints) is properly supposed to be taken to refer to the Levenstein distance of a conventional (or explicitly specified) binary encoding of the two strings.

<comment><USER7> Can you point to a source that defines Levenstein distance as only referring to bitstreams?

A translation of the original article [1] that introduced the concept notes in a footnote that "the definitions given below are also meaningful if the code is taken to mean an arbitrary set of words (possibly of different lengths) in some alphabet containing r letters (r >= 2)".

And if you wish to strictly stick to how it was originally defined, you'd need to only use strings of the same length.

More recent sources [2] say instead "over some alphabet", and even in the first footnote, describe results for "arbitrarily large alphabets"!

[1] [https://nymity.ch/sybilhunting/pdf/Levenshtein1966a.pdf](https://nymity.ch/sybilhunting/pdf/Levenshtein1966a.pdf)

[2] [https://arxiv.org/pdf/1005.4033.pdf](https://arxiv.org/pdf/1005.4033.pdf)

<comment><USER5> And Unicode is the biggest alphabet haha.

</comment></comment></comment></comment></comment></comment><comment><USER8> The Rust version uses `target.chars().count()` to initialise the cache, while the Go version counts up to `len(target)`. These are not equivalent: the Rust version counts Unicode code points, the Go version counts bytes.

I am confused by the implementations, although I have not spent any time testing them. Both versions contain a mix of code that counts bytes (`.len()` and `len(...)`) and Unicode code points (`chars()` and `[]rune(...)`). My guess is that the implementation might not work correctly for certain non- ASCII strings, but I have not verified this.

Of course, if only ASCII strings are valid as input for this implementation then both versions will be a lot faster if they exclusively operate on bytes instead.

<comment><USER9> Yep.

Here a Go playground example showing that the result is indeed wrong:

[https://play.golang.org/p/vmctMFUevPc](https://play.golang.org/p/vmctMFUevPc)

It should output 3 but outputs 5 because each ö is two bytes, len("föö") = 5.

I would suggest using "range" to iterate over the unicode characters.

<comment><USER10> If you diff föö and f though, it correctly gives an edit distance of 2.

The code is weird because someone knew enough to convert the strings to slices of runes but not enough to use the rune slices consistently. :-/

<comment><USER5> Not to mention Rune slices are insufficient for things like Flag emoji and Family emoji, which is going to be a ton of separate runes put together. The latter of which, apparently deletes one family member at a time when you hit "backspace".

<comment><USER11> Oh fab, just when I thought I had a fairly solid understanding of how to handle Unicode strings I learn something else that increases the complexity.

I have nothing but respect and gratitude for people that write good unicode handling libraries, but even then the end developer has to learn a lot just to be aware of what to look out for when handling strings.

Somewhere on github I think, somebody has posted a file with evil Unicode strings.

<comment><USER5> In general, unicode requires you think differently about strings depending on context. Here's my rule of thumb.

1\. If you are transporting a unicode string, reading/writing over the network or to a file, think in terms of UTF-8 bytes. Do not attempt to splice the string, treat it as an atomic unit.

2\. If you are parsing a string, think in terms of code points (runes in Go, chars in Rust). A good example would be the Servo CSS parser. [1]

3\. If you're comparing/searching/inspecting/sorting a string in code, segment by grapheme clusters and normalize, then do what you came to do. [2]

4\. If you're displaying a string, think in terms of pixels. Do not attempt to limit a string by length in "characters" (nee grapheme clusters in the unicode world) but rather measure by what the renderer does with the string. Each character can be a thoroughly arbitrary width and height.

5\. If you're building a WYSIWYG editor, there's more to it than I even know myself, but I suggest reading into what Xi did. It's going to be some combination of everything above. [3]

[1] [https://github.com/servo/rust- cssparser/blob/master/src/toke...](https://github.com/servo/rust- cssparser/blob/master/src/tokenizer.rs)

[2] [https://github.com/unicode-rs/unicode- segmentation](https://github.com/unicode-rs/unicode-segmentation)

[3] [https://github.com/xi-editor/xi-editor](https://github.com/xi-editor/xi- editor)

<comment><USER12> > 2\. If you are parsing a string, think in terms of code points (runes in Go, > chars in Rust). A good example would be the Servo CSS parser. [1]

If all your syntactically meaningful characters are in ASCII you can also use UTF-8 bytes in your parser.

Even if they aren't, no UTF-8 encoding of a character is a substring of the encoding of any other character(s).

</comment></comment></comment></comment></comment></comment></comment><comment><USER13> I tried to benchmark Go/Rust versions as well.

I made 4 changes in Rust version.

1\. Moved up the line that gets a value from cache[j+1] before any calls are made to cache[j]. This removes 1 bound check. (Improvement from 182,747ns down to 176,xyzns +-4800)

2\. Moved from .chars().enumerate() to .as_bytes() and manually tracking current position with i/j variables. (Improvement from 176,xyz ns down to 140,xyz ns)

3\. Moved to the standard benchmark suite from main + handrolled benchmark system.(File read + load + parse into lines was kept out of benchmark)

4\. Replaced hand rolled min with std::cmp::min. (improvement from 140,xyz down to 139,xyz but the std deviation was about the same. So Could just be a fluke. Don't know)

In Go version, I made three changes.

1\. Doing the same thing from #1 in Rust actually increased the runtime from 190,xyz to 232,xyz and quite consistently too. I ran it 10+ times to confirm)

2\. Replaced []rune(source), []rune(target) to []byte(source), []byte(target). (Improvement from 214817ns to 190152 ns)

3\. Replaced hand rolled bench mark system with a proper bench mark system in Go. (Again, File read + load + parse into lines was kept out of benchmark)

So, At the end of it, Rust version was about 50k ns faster than Go version.

Edit #1:

In rust version, I had also replaced the cache initialization to (0..=target.len()).collect() before doing anything els.. This also gave a good perf boost but I forgot to note down the exact value.

<comment><USER14> I'd be really surprised to hear that Go is supposed to be faster than Rust. Of course Rust is a bit newer but to me it always sounded like Go is fast because it's static but it doesn't have to be high-speed if that would sacrifice conciseness. Given that this is an artifical example, this here looks more realistic: [https://benchmarksgame- team.pages.debian.net/benchmarksgame/...](https://benchmarksgame- team.pages.debian.net/benchmarksgame/fastest/rust-go.html)

<comment><USER5> Rust and Go are contemporaries. Rust started in 2006 at Mozilla, and the first Go public release from Google was in 2009, meaning it probably started at the same time.

Except of course all the Plan 9 garbage (like Go's hand-rolled assembler) brought in to underpin Go from the 80s ;)

<comment><USER15> > Except of course all the Plan 9 garbage (like Go's hand-rolled assembler) > brought in to underpin Go from the 80s

This is unfair criticism. If Go had used LLVM, it would affect its selling point (fast compile times) and authors knew the plan 9 toolchain well.

Go the language feels like it is from 80s. But its toolchain is not at all bad. LLVM monoculture is the last thing one would want. Obligatory reminder that LLVM has its flaws too..

<comment><USER14> Also when this LLVM stuff doesn't work, it's a major pain to troubleshoot because all this is a complexity monster. Go and its Plan 9 heritage is more like 80s retro future and things like cross-compiling are super easy

<comment><USER5> In my opinion, compile times are irrelevant. Developers can always get faster or larger machines if they need them. What matters is how the final product performs on customer machines. Performance and memory usage of the final product, plus your ability as an engineering team to avoid costly and difficult mistakes are basically the only thing that matters.

<comment><USER15> > Developers can always get faster or larger machines if they need them.

Those who can get top notch hardware are already on top notch hardware and those who can't are limited by management decisions.

Even with top notch machines, compile times matter. Because the difference are huge in compile times of C++ and Go in moderately complex projects with bad build systems that are the norm.

> What matters is how the final product performs on customer machines.

Apparently today's developers value fast iteration speed and that's fine. The problem is they don't value user resources because they have top notch machines and don't care about performance.

</comment></comment></comment></comment></comment></comment></comment><comment><USER16> I recently did some experiments with creating small static Rust binaries, custom linking, no_std et cetera. A lot of stuff around that kind of thing is unstable or unfinished, which might be somewhat expected. But I've also come to the conclusion that Rust relies on libc _way_ too much. That might be fine on Linux, where GNU's libc is well-maintained, is a bit questionable on MacOS (as seen in this article) and is a a complete distribution nightmare on Windows (in no small part due to a series of very questionable decisions by Microsoft).

My understanding is that Go doesn't use the libc at all and makes system calls directly, which IMO is the correct decision in a modern systems programming language that doesn't want to be limited by 40 years of cruft.

<comment><USER17> As far as I know, the official system interface on Windows and several Unix systems is via the standard library, not via direct syscalls. I don't know about the MacOS. But in general, you may be required to dynamically link the standard library on many platforms.

Linux guarantees syscalls are stable. And on Linux, you have the option of telling Rust to cross-compile using a statically-linked musl-libc. (If you also need to statically link OpenSSL or a few other common libraries, I maintain [https://github.com/emk/rust-musl- builder](https://github.com/emk/rust-musl-builder), and there's at least one similar image out there.)

<comment><USER18> AFAIK on Windows, the hierarchy is:

C library => kernel32.dll => ntdll.dll => system calls

You don't have to go via the C library - calling kernel32 directly is fine (I believe this is what Go does). However, it's very rare to call ntdll or to make system calls directly.

<comment><USER16> Basically yes. ntdll is an implementation detail that shouldn't be relied upon. kernel32/user32 and friends are considered the "proper" interface to the system and have been stable for decades.

<comment><USER19> There are some ntdll calls that are officially documented and ok to use. Of course there are also a lot of calls you shouldn't use.

When necessary, it's fine to use even undocumented ones to support Windows 7 and older. It's not like those are going to change anymore.

</comment></comment></comment></comment></comment><comment><USER20> The main problem is that allocating a vector for each evaluation is completely wrong: instead, it needs to be allocated once by making the function a method on a struct containing a Vec; which makes the allocator moot.

The second problem is that at least the Rust code is decoding UTF-8 every iteration of the inner loop instead of decoding once and saving the result, or even better interning the characters and having versions of the inner loop for 32-bit chars and 8-bit and 16-bit interned indexes.

Furthermore the code rereads cache[j] instead of storing the previous value, and doesn't do anything to make sure that bound checks are elided in the inner loop (although perhaps the compiler can optimize that).

The code for computing the min seems to have been written mindlessly rather than putting serious thought towards whether to have branches or not and in what order (depending on an analysis of what the branch directions rates would be).

Implausible benchmark results are almost always an indicator of the incompetence of the person performing the benchmark.

<comment><USER21> > The second problem is that at least the Rust code is decoding UTF-8 every > iteration of the inner loop instead of decoding once and saving the result

Indeed. This is a pretty damning difference. The `target` string is being repeatedly UTF-8 decoded where as the same is not true in the Go version. The Go version even _goes out of its way_ to do UTF-8 decoding exactly once for each of `source` and `target`, but then doesn't do the same for the Rust program.

> Implausible benchmark results are almost always an indicator of the > incompetence of the person performing the benchmark.

Come on. We can do better than this. Please don't make it personal. We all have to learn things at some point.

<comment><USER22> > Indeed. This is a pretty damning difference. The `target` string is being > repeatedly UTF-8 decoded where as the same is not true in the Go version. > The Go version even goes out of its way to do UTF-8 decoding exactly once > for each of `source` and `target`, but then doesn't do the same for the Rust > program.

I'm really not sure that's an issue, utf8 decoding is very, very cheap and it's iterating either way.

It would have to be benched, but I wouldn't be surprised if allocating the caches (at least one allocation per line of input) had way more overhead, especially given the inputs are so very short.

I'm not going to claim Rust's utf8 decoder is the fastest around, but it's _very_ fast.

<comment><USER21> It's cheap, but not _that_ cheap. It shouldn't be as cheap as just iterating over a sequence of 32-bit integers.

But yes, I did benchmark this, even after reusing allocations, and I can't tell a difference. The benchmark is fairly noisy.

I agree with your conclusion, especially after looking at the input[1]. The strings are so small that the overhead of caching the UTF-8 decoding is probably comparable to the cost of doing UTF-8 decoding.

[1] - [https://github.com/christianscott/levenshtein-distance- bench...](https://github.com/christianscott/levenshtein-distance- benchmarks/blob/master/sample.txt)

<comment><USER2> > It shouldn't be as cheap as just iterating over a sequence of 32-bit > integers.

I wonder if there are any benchmarks about this? Specifically, it feels like in theory iterating utf8 _could_ actually be faster if the data is mostly ascii, as that would require less memory bandwidth, and it seems like the computation is simple enough for memory to be the bottleneck (this is a wild guess, I have horrible intuition about speed of various hardware things). In _this_ particular benchmark this reasoning doesn't apply, as strings are short and should just fit in cache.

<comment><USER21> If all you need to do is validate UTF-8, then yes, mostly ASCII enables some nice fast paths[1].

I'm not a UTF-8 decoding specialist, but if you need to traverse rune-by-rune via an API as general as `str::chars`, then you need to do some kind of work to convert your bytes into runes. Usually this involves some kind of branching.

But no, I haven't benchmarked it. Just intuition. A better researched response to your comment would benchmark, and would probably at least do some research on whether Daniel Lemire's work[2] would be applicable. (Or, in general, whether SIMD could be used to batch the UTF-8 decoding process.)

[1] - [https://github.com/BurntSushi/bstr/blob/91edb3fb3e1ef347b30e...](https://github.com/BurntSushi/bstr/blob/91edb3fb3e1ef347b30e5bd792bb4d29ee19d163/src/ascii.rs#L1)

[2] - [https://lemire.me/blog/2018/05/16/validating- utf-8-strings-u...](https://lemire.me/blog/2018/05/16/validating- utf-8-strings-using-as-little-as-0-7-cycles-per-byte/)

</comment></comment></comment></comment></comment></comment><comment><USER23> Missed chance to shorten title to "Making Rust Go Fast" :-)

<comment><USER24> Making Rust As Fast As It Can Go

<comment><USER25> Go Rust!

<comment><USER5> GO!!!!

</comment></comment></comment></comment><comment><USER26> Is it intentional that the benchmarks include not only running the program itself but also compiling it? e.g. in the linked source code, the bench.sh includes the compilation step which is known to be slow in rust:

        #!/usr/bin/env bash

        set -e
        run() {
          cargo build --release 2> /dev/null
          ./target/release/rust
        }

        run;

Sure, if you run it many times in succession the compiler won't do much but the benchmarking script (run.js) doesn't really indicate that and the blog post also doesn't mention that.

EDIT: I was just being stupid, don't mind me. The times were taken within each language and not externally.

<comment><AUTHOR> run.js is not doing the benchmarking. If you look at the source for each of the programs being benchmarked, you'll see that the programs themselves are responsible for benchmarking

</comment></comment><comment><USER27> Could also try to use the smallvec crate in this case, which put small allocation on the stack [https://docs.rs/smallvec/](https://docs.rs/smallvec/)

</comment><comment><USER5> There's a bunch of issues with the Rust implementation, not least that the initial condition where source or target lengths are zero, it returns the number of UTF-8 bytes of the other, but all other computations are performed in terms of Unicode chars -- except at the end: `cache[target.len()]` which will return the wrong value if any non-ASCII characters precede it.

Further, each time you call `.chars().count()` the entire string is re- enumerated at Unicode character boundaries, which is O(n) and hardly cheap, hence wrapping it in an iterator over char view.

Also, re-implementing std::cmp::min at the bottom there may well lead to a missed optimization.

Anyways, I cleaned it up here in case the author is curious: [https://gist.github.com/martinmroz/2ff91041416eeff1b81f624ea...](https://gist.github.com/martinmroz/2ff91041416eeff1b81f624ea585f83a)

</comment><comment><USER28> I'm surprised that a naive implementation in Go can outperform a naive implementation in Rust.

<comment><USER29> I'm not. Hell, when I first started learning rust i frequently wrote code that ran slower than _python_.

</comment></comment><comment><USER30> I tried this on a spare time project[1]. Runtime in a quick test went down from 14.5 to 12.2 secs on macOS!

So a solid ~15% by changing the allocator to jemalloc.

However, I now have a segfault w/o a stack trace when the data gets written at the end of the process.

Possibly something fishy in some `unsafe{}` code of a dependent crate of mine that the different allocator exposed. :]

Still – no stack trace at all is very strange in Rust when one runs a debug build with RUST_BACKTRACE=full.

[1] [https://github.com/virtualritz/rust-diffusion-limited- aggreg...](https://github.com/virtualritz/rust-diffusion-limited-aggregation)

<comment><USER31> I have found that jemallocator is currently broken on macOS Catalina, so that might be the problem. If you can reproduce this issue reliably, I'd love to hear about it because I can't myself unless I use a very specific toolchain that produces -O3 binaries that are a real pain to work with.

<comment><USER30> It's 100% reproducible. Just check out the previous to last commit on master on the github repo I linked to and run the tool with any command that invokes the nsi crate.

Eg.:

       > rdla dump foo.nsi

should produce the segfault before exiting the process.

Is there a jemallocator ticked where to attach a report for this?

<comment><USER31> Thanks! I think you found the jemallocator bug, so I'll try your project and follow up there.

<comment><USER30> I think that bug may be fixed in jemalloc by now. The version of jemalloc that the jemalloc-sys crate tracks is from two years ago. I tried bumping jemalloc to latest master but that makes the jemalloc-sys build fail (trivially with a missing script but still).

Also there is this: [https://github.com/gnzlbg/jemallocator/issues/136](https://github.com/gnzlbg/jemallocator/issues/136)

</comment></comment></comment></comment></comment><comment><USER32> Impressed to see four posts about Rust on the front page of HN simultaneously.

</comment><comment><USER33> I've found that Microsoft's mimalloc (available for Rust via [https://crates.io/crates/mimalloc](https://crates.io/crates/mimalloc)) typically provides even better performance than jemalloc. Of course, allocator performance can vary a lot depending on workload, which is why it's good to have options.

</comment><comment><USER34> This discussion seems to me like a microcosm of the differences in philosophies between Rust and Go.

With Rust, you have much more control, but you also need a deep understanding of the language to get the most out of it. With Go, the way you think it should work is usually is Good Enough™.

<comment><USER35> I wouldn't put it that way. Both languages are fast at nice straight-line code.

The main area I'd expect to see performance benefits for rust (though I don't have experience here) is larger rust programs. Rust's zero-cost abstractions have more benefits as the abstractions nest more deeply. For a small program, you don't really have a lot of abstractions, so Go will do just fine.

I think Go has a number of nice performance tricks up it's sleeve, though, so I wouldn't rule out Go on performance grounds too quickly.

</comment></comment><comment><USER36> It may be that the system allocator is making an excessive number of syscalls to do work, whereas most custom allocators will allocate in slabs to avoid this. You could try using dtruss or strace to compare the differences.

</comment><comment><USER34> A few folks have commented that there were logic errors in the Go version. Specifically that

      len("föö") = 5

should instead have returned

      len("föö") = 3

I submitted a pull request, [https://github.com/christianscott/levenshtein- distance-bench...](https://github.com/christianscott/levenshtein-distance- benchmarks/pull/3), that fixes these issues in the Go implementation.

Interestingly enough, when I re-ran the benchmark, the Go version is roughly 19% faster than it was previously:

      old: 1.747889s
      new: 1.409262s (-19.3%)

</comment><comment><USER37> Related thread: [https://news.ycombinator.com/item?id=21103027](https://news.ycombinator.com/item?id=21103027)

</comment><comment><USER38> FreeBSD's system allocator _is_ jemalloc :-).

</comment><comment><USER39> TLDR for people who didn't read:

The speed difference came from the allocator.

Rust switched from jemalloc to the system allocator per ticket #36963[0] for various reasons (like binary bloat, valgrind incompatibility, etc...).

Go uses a custom allocator[1] instead.

To make 'Rust Go fast' (pun intended), one can use the '#[global_allocator]' to use a custom allocator (in this case, with the jemallocator crate) to make allocations fast again.

[0]: [https://github.com/rust-lang/rust/issues/36963](https://github.com/rust- lang/rust/issues/36963)

[1]: [https://golang.org/src/runtime/malloc.go](https://golang.org/src/runtime/malloc.go)

<comment><USER40> The comments of Rust programmers here also suggest that the Rust implementation is, indeed, different from the Go implementation.

<comment><USER39> It was just a summary of the post contents - the post suggests that the biggest difference comes from the allocator.

</comment></comment></comment><comment><USER41> Assuming this was run on a 64bit system, the Rust version seems to be allocating and zeroing twice as much memory as the Go version.

edit: this has been pointed out as incorrect, Go ints are 8 bytes on 64bit systems -- thanks for the correction!

      let mut cache: Vec<usize> = (0..=target.chars().count()).collect();

which can be simplified as

      let mut cache: Vec<usize> = vec![0; target.len()];

vs

      cache := make([]int, len(target)+1)
      for i := 0; i < len(target)+1; i++ {
        cache[i] = i
      }

Rust usize being 8 bytes and Go int being 4 bytes as I understand it.

So between doing more work and worse cache usage, it wouldn't be surprising if the Rust version was slower even with the faster allocator.

<comment><USER42> Go int is 8 bytes

<comment><USER9> It can be either depending on the system.

[https://golang.org/ref/spec#Numeric_types](https://golang.org/ref/spec#Numeric_types)</comment></comment></comment>


Research Based on the .NET Runtime (2019) - <AUTHOR> http://www.mattwarren.org/2019/10/25/Research-based-on-the-.NET-Runtime/ <comment><USER2> [https://github.com/dotnet/runtime/tree/master/docs/design/co...](https://github.com/dotnet/runtime/tree/master/docs/design/coreclr/jit)

</comment><comment><USER3> The research papers on manual memory management for .NET looked interesting. I wonder if anything ever came of it?

<comment><USER4> The delete/free bits never made it in, but some related stuff definitely has made its way into the standard library and compiler over time. Span<T> and Memory<T> provide a bunch of affordances for safely mixing GC and non-GC pointers in the same workflow. Big chunks of the BCL can operate on strings and data at any address in memory now, which is really helpful for high- throughput parsing and streaming.

<comment><USER5> What's an affordance? Is it something with a technical meaning, like pragma?

<comment> Const-me On technical side, I think GP meant stuff like this [https://docs.microsoft.com/en- us/dotnet/api/system.span-1.-c...](https://docs.microsoft.com/en- us/dotnet/api/system.span-1.-ctor?view=netcore-2.1#System_Span_1__ctor_System_Void__System_Int32_) And this: [https://docs.microsoft.com/en-us/dotnet/csharp/language- refe...](https://docs.microsoft.com/en-us/dotnet/csharp/language- reference/operators/stackalloc)

<comment><USER5> I know about spans and stackalloc, I mean quite literally does the word affordance mean something technical here that I've not come across?

<comment><USER6> Affordance means that the interface of something indicates clearly how it's supposed to be used and for what purpose. This is broader term and it applies to physical stuff like door handles or knife grips too. It's intuitive enough which is probably why previous poster was confused about your question.

<comment><USER5> I've come across the term affordances before, but in this context if they'd been referred to as methods or classes or 'ways for safely mixing...' whatever I'd have got it, no problem. Calling them affordances is correct (technically they are) but made me think there was something vital (edit: perhaps .net related) I was missing. Mind at peace now, thanks all.</comment></comment></comment></comment></comment></comment></comment>


3 Ways to Try Out Linux, For a Windows User - <AUTHOR> http://www.linuxhaxor.net/2008/06/16/3-ways-to-try-out-linux-for-a-windows-user/

<comment><USER2> Anything that encourages Linux use (especially by Windows users) is good.

Until people are exposed to other systems, they will never really appreciate how poor Windows is at many things.</comment>


A Wiki Entry for Every Single Address in the World - <AUTHOR> http://www.placenotes.org

<comment><USER2> I don't think it's even worth beginning to invite participation for a project like this until you have at least built the minimal mandatory user interface: a map-based interface that _at least_ shows which locations have entries.

<comment><AUTHOR> Maybe. I'm mostly of the opinion that the map-based user interface is often a bit clumsy and slow, i.e. Wikimapia and can be more of a burden than a blessing.

<comment><USER2> But they are an essential option for displaying geographic data. You may have an entry for something 6 blocks from me, and how would I ever know about it?</comment></comment></comment>


Oracle Pits GraalVM Against Google Go - <AUTHOR> https://www.javaworld.com/article/3440103/oracle-pits-graalvm-against-google-go.html <comment><USER2> If it didnt have such awkward "support" for reflection and missing support for key methods in Java, GraalVM would be much more likely to be adopted</comment>


My eBay Job: Do 1.3 million people really earn their living from the auction site? - <AUTHOR> http://www.slate.com/toolbar.aspx?action=print&id=2191907

<comment><USER2> This article comes so close to telling us what we actually want to know, and then doesn't. Why no numbers on how many people are in each (bronze, silver, etc) tier?

<comment><USER3> I think you'd have to read the ACNielsen report to get that number (if it was published). The fact that they have a number like "X people derive primary _or secondary_ income" suggests that it isn't even in the final report.

</comment></comment><comment><USER4> i feel like this number would be more correct if it includes things like ebay, ebay's subsidiaries, companies built around ebay, people who work at drop shipping companies, people who have stores that make use of ebay as a distribution channel, etc. etc..

one could only hope that this is what its talking about</comment>


The HEY discussion has revolved around money, but this is not about money - <AUTHOR> https://hey.com/apple/iap/ <comment><USER2> " You can no longer help the customer who's buying your product with the following requests: Refunds, credit card changes, discounts, trial extensions, hardship exceptions, comps, partial payments, non-profit discounts, educational discounts, downtime credits, tax exceptions, etc. You can't control any of this when you charge your customers through Apple's platform. So now you're forced to sell a product - with your name and reputation on it - to your customers, yet you are helpless and unable to help them if they need a hand with any of the above."

<comment><USER3> These statements are so true. When our company had billing issues with in-app purchases, there were plenty of times that we wanted to refund people for their purchases and we had no way to do this on both Apple and Amazons platforms. We had to send them to Apple and Amazon customer service and we never knew if they got help or not. Google on the other hand actually made it very easy for us to find someone's purchase and issue them a refund, but even with Google you are very removed from the customer and can't directly contact them or find out much about the customer.

</comment></comment><comment><USER4> From the post:

 _When someone signs up for your product in the App Store, they aren't technically your customer anymore - they are essentially Apple's customer. They pay Apple, and Apple then pays you. So that customer you've spent years of time, treasure, and reputation earning, is handed over to Apple. And you have to pay Apple 30% for the privilege of doing so!_

Very true. I think people tend to associate in-app purchases with Apple unless they're very sharp or very motivated to do otherwise.

Does anyone think Apple will address this in any form at WWDC next week?

<comment><USER5> What's wrong with that? I trust Apple more to cancel a subscription correctly and stop billing me than most other companies.

<comment><USER4> I think Jason Fried does a good job of explaining what's wrong with it in the post:

 _[...] at Basecamp we help people for all sorts of reasons. We apply credit to accounts for all sorts of reasons. We provide hardship exceptions for all sorts of reasons. We discount our software for teachers. We provide free versions for first responders. We extend trials for those who need more time. We extend payment terms occasionally for those who can't make ends meet this month. We make exceptions because people are exceptional. We take enormous pride in helping people out. And we're damn good at it._

 _If we had to push our customers through Apple's system, we couldn't do any of that. Apple's rules prevent us from servicing our customers, yet Apple gives us no choice but to submit to those onerous rules or not be represented on their platform. [...]_

This reflects my experience as well. If I can help customers with their billing issues, it makes my company look really good – we get an opportunity to provide exceptional customer service. If their subscriptions are managed via Apple, we don't really get that opportunity; instead, we're just one of many apps that can't make exceptions.

Apple is far less likely than the company that builds the app to care about the sorts of individual customer reasons for needing a trial extended, account credited, etc, that are mentioned in the article.

Yes, lots of companies are bad at customer service. But some are really good! (you can tell by how their customers gush about them.) And with forced in-app purchases, the good ones don't get the opportunity to do their best work.

<comment><USER6> As much as I believe that Basecamp will do the right thing for their customers the harsh reality is that the vast majority of app developers suck and their subscription system will end up looking more like: "We accidentally leak your credit card info. We accidentally double charge you. We refuse to cancel your subscription when you want to because you signed a hidden one year contract."

Apple's subscription restriction is optimized to prevent the bad actors in the ecosystem from abusing Apple customers. And unfortunately there are a lot more bad actors in the marketplaces than there are good actors who are trying to do the right thing.

If it was a perfect system then yes it would be nice for Apple to allow third parties to have their own subscription systems. But I'd rather be locked into Apple subscriptions than deal with the nefarious and poorly implemented custom subscription systems of the average shovelware app developer.

<comment><USER7> Apple has always treated their consumers with kid gloves. As a user, I would like the choice to purchase via Apple and via the developer directly. There are apps and people I trust and there are those I do not. Apple subscriptions would still be valuable for developers that haven't established trust yet.

If an app has a poor subscription system, I just wouldn't subscribe. That would eventually push them towards Apple or towards a better system.

As consumers we are hurt because we don't get the experience of any subscription services that are better than Apples because the decision is made for us.

</comment></comment></comment></comment></comment><comment><USER8> All their criticisms seem sound!

There's a (good) theme in this: control. They don't want Apple to control the relationship between Hey and its customers. Which is _super cool_. I like it. It's bold, it's correct, it's an easy position to hold.

Which is why it's weird they don't have domain support. Why should I cede control of the relationship between my contacts and myself to them? I get the feeling domain support will be part of the coming business plan mentioned in the welcome emails when you sign up. So you can spend the $99/year, or more with the "premium" names, and still depend on Hey to control that relationship. I was soured on it a bit by not being able to get kye@. That's my name. I don't have almost $400/year to claim it. Someone more fortunate will be able to. I already have a domain for the name that's easy to remember.

Everyone loved Gmail when it came out. Having a Gmail address was a point of pride for at least as long as invites were scarce. Who thinks that now? Controlling the domain is a hedge against the risk that Hey will go down the same road. It should have been there at launch, and it should be available on _all_ plans. I would have signed up the moment I got in and saw how well everything is put together.

I like it a lot, but I can't possibly use it if I can't point it at a domain I control at the plan level that's right for my needs.

</comment><comment><USER9> Initially It was the selective enforcement that got me, how Fastmail and other mail client didn't get IAP treatment. Turns out they did [1]. Although it is not clear how "recent" that treatment was, if it was before or after the Hey drama.

[1] [https://twitter.com/Fastmail/status/1273800222989324288](https://twitter.com/Fastmail/status/1273800222989324288)

</comment><comment><USER10> It is about the money for Apple. That is all it is about.

What's next? Are they going to want a 30% cut of any transactions I make through the Vanguard or E-Trade apps?

</comment><comment><USER11> I'm q bit tired of this HEY marketing spin by now. And it's the same old story, over and over.

If you build on a platform, be prepared to get fucked over anytime, period. Otherwise build on a protocol, that is open, and will prevail.

<comment><USER12> There's no way to entirely avoid platform risk, unless you're running your app off the grid, on a boat in international waters, using your own hardware, getting online via multiple satellite providers, and taking payments only via Bitcoin.

Otherwise, it's about trying to decide what risks are reasonable, and doing what little you can to influence each platform's behavior toward you, which is what Basecamp is doing.

<comment><USER13> Web apps are pretty risk free in this sense. Nobody controls the web.

<comment><USER14> Sure, but the platforms can still go out of their way to make it infeasible to build on the web - There's no way that Hey could have been a web app on iOS since Safari refuses to support a lot of functionality that would bring web apps to parity with native ones.

</comment></comment></comment></comment><comment><USER15> Something you'll notice during every WWDC is that Apple will repeatedly say that iOS updates are "always free." I always found that odd till I realized it both a throwback to the days when upgrades weren't free - but also as a nod to "We don't get paid by charging for our operating system." Once you're entering the Apple ecosystem - Apple is making itself the de-facto middleman between the user and everyone else.

I think that's what this comes down to - you're paying Apple the premium as a developer to access Apple's customers.

They aren't your customers. They are Apples. So this argument calls this out but is asking them to change that? Seems like they are asking Apple to completely change their business model.

Edit to add from the TC interview with Schiller, he even gives a way to get around the app store process, which I have used in a previous app that we did on iOS:

"One way that Hey could have gone, Schiller says, is to offer a free or paid version of the app with basic email reading features on the App Store, then separately offered an upgraded email service that worked with the Hey app on iOS on its own website"</comment>


Ask HN: Is there a command line domain register? - <AUTHOR> I've used godaddy, dreamhost, etc. etc. I hate their UI. Is there a 100% command line domain registrar where I can set DNS and zone and everything right from terminal. And if not, isn't that a great idea? <comment><USER2> Although I have used the CLI for doing so, I believe that you can do everything you want with AWS Route53.

[http://docs.aws.amazon.com/Route53/latest/APIReference/Welco...](http://docs.aws.amazon.com/Route53/latest/APIReference/Welcome.html)

</comment><comment><USER3> Enom.com, for one, offers a _very_ straightforward API, that lets you register a domain and setup DNS with a single HTTP request (provided that you have payment info already saved in their backend).

I'm _sure_ there are others, Enom is just the one I've used at work for this purpose.

<comment><AUTHOR> thanks, I'm gonna try [https://www.hover.com](https://www.hover.com) too. I've heard they are the new modern one.</comment></comment>


When Does Point-Free Notation Make Code More Readable? - <AUTHOR> https://spin.atomicobject.com/2017/09/29/point-free-notation/#.Wc5PtNDfhoc.hackernews <comment><USER2> Okay, I get the concept. Where is the argument for it?

>Sometimes, especially in abstract situations involving higher-order functions, providing names for tangential arguments can cloud the mathematical concepts underlying what you're doing.

Sometimes? When?

>point-free notation serves as an example of how subtle changes to the way you think about or define your code can have big impacts on readability.

In the end, we're defining `atLeastTwo` and using it identically, so it would be much more compelling if there was an example in which things were, in fact, more readable. This example is just syntactic. It's like arguing that `const atLeastTwo = (x) => x >= 2` is better than `function atLeastTwo(x) { ... }`

> It's your job as a developer to be cognizant of these tradeoffs.

It's just that this piece isn't really helping me get there.

I think the author is suggesting that we define `atLeastTwo` in terms of an `atLeast` primitive, i.e., currying and partial application. It's clear that this is going to make composition easier, but it is relying on it. Now, I'd like to see how point-free, as a paradigm, yields better results in terms of readability or error prevention.

<comment><USER3> Concatenative languages come to mind. You basically don't have to think about partial application ever again, because you get it "for free" (at the cost of reverse Polish notation). Forth, Joy, Factor and Kitten are all examples of languages based on this paradigm.

[0] [https://en.wikipedia.org/wiki/Forth_(programming_language)](https://en.wikipedia.org/wiki/Forth_\(programming_language\))

[1] [https://web.archive.org/web/20111007025556/http://www.latrob...](https://web.archive.org/web/20111007025556/http://www.latrobe.edu.au/phimvt/joy/j02maf.html)

[2] [http://factorcode.org/](http://factorcode.org/)

[3] [http://kittenlang.org/](http://kittenlang.org/)

<comment><USER4> Indeed, concatenative languages are a perfect example of point-free notation. I don't see the point (pun!) of doing it in JS or any mainstream language though.

They are also a joy to design/implement* and doing it really changes how you think about programming.

* [http://hashmal.github.io/shirka/](http://hashmal.github.io/shirka/) my personnal attempt at designing such a language. warning: very old, dirty, buggy code.

</comment></comment></comment><comment><USER5> "JavaScript doesn't include this out of the box, but it's an interesting exercise to write your own."

I may be misunderstanding point-free notation (I had never heard of it before), but I think `bind` ([https://developer.mozilla.org/en- US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en- US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)) let's a programmer provide some of the arguments to a function and returns a new function that requires only the remaining arguments.

<comment><USER6> It's also easy to implement as a one liner

       var atleastTwo = function(x){return function(){return Math.max(2,x);}

Even simpler with typescript

       var atLeastTwo = (x) => (() => Math.max(2, x))

<comment><USER7> ES6

        const atLeastTwo = x => Math.max(2,x)

<comment><USER8> his is a function that returns a function, i.e. you'd need to call atLeastTwo(x)(); to get a result, yours is just a function. in es6 the equivalent thing to what he wrote would be:

        const atLeastTwo = x => () => Math.max(2, x);

</comment></comment></comment></comment><comment><USER9> Why do we need yet another term for concepts already known as function composition, currying, and partial application?

<comment><USER10> Pointfree style _uses_ composition (among other operators), currying, and partially applied functions, but it refers to something distinct from the sum of those concepts/features.

Consider the following function of two parameters, written in Haskell/PureScript syntax:

       f x y =  g x (m (h y))
             = (g x . m . h) y

We can now eta-reduce/partially apply f instead in the definition (which is possible because f is curried by default):

       f x   = g x . m . h

If I were using pointfree style at all (which I almost never do), this is where I'd stop. However, you can go the whole hog and strip out all the arguments:

             = g x . (m . h)
             = ((. (m . h)) . g) x

       f     = (. (m . h)) . g

I've made use of everything you listed, but it's only when you go this far that your function is truly free of "points".

<comment><USER11> _Pointfree style uses composition (among other operators), currying, and partially applied functions, but it refers to something distinct from the sum of those concepts /features_

Exactly. But in all fairness to the parent comment to yours, the original article doesn't convey this as well as you lucidly did, and his confusion is perfectly understandable.

<comment><USER10> Thanks! I'd say at least some of the blame for that is due to how C-family languages make these concepts appear somewhat ... opaque.

</comment></comment></comment></comment><comment><USER12> Point-free notation builds a program indirectly using expressions, rather than declaratively and directly like most programming languages. You need to follow data and control flow in order to understand the program that is built.

I think it's rarely easier to read or understand, with a few major exceptions. Certainly toy examples like in the article are too simple to highlight the benefits.

If the blocks being composed are reused and recombined a lot, but are meaty enough not to be inlined, then the composition effectively turn into a kind of high-level DSL. Consider e.g. a data processing pipeline using relational operators; filters, joins, sorts, projections etc. Those things have meaty implementations, but fairly simple interfaces; they're tailor-made for composing into larger abstractions that have the same external interfaces.

If the problem being solved inherently requires building a custom program to solve it, then it's hard to avoid writing something that isn't isomorphic to point-free style. For example, a reporting tool that doesn't dump the user in a SQL editor.

</comment><comment><USER13> I don't really find the argument convincing. I'd argue that the original approach was more intuitive.

<comment><USER14> "Intuitive" is too subject.

What we're interested in is _simplicity_. And this is an objective measure.

This (A):

       partial(Math.max, 2)

is _objectively_ simpler than this (B):

       function atLeastTwo(someNumber) {
          return Math.max(2, someNumber);
       }

Why is it objective? Because it expresses the _same_ exact value (i.e., function) with an objectively fewer number of concepts.

When (A) is unfamiliar it feels less intuitive, but with practice/experience that sense of unintuitive goes away, and then you are left with (B) a _simpler_ expression -- and therefore less cognitive load on the developer trying to make progress.

<comment><USER15> A is objectively more complex:

B gives you all the information you need to know what's going on.

A requires you to go dig up what "partial" is doing (which, it turns out is itself doing some wacky gymnastics to turn one of its arguments into a function and the other into a parameter (and the parameter of whatever gets passed into the returned function as the other parameter of the function that was originally passed in).

Observe how much writing it took just to describe what's going on with your "A". No explanation at all is required for B. It's complete as written. Simple, and intuitive all in one.

<comment><USER16> "partial" is an independent concept, which could be learned once and applied after that just as "function" can be in example B. You don't say that "function" and "return" are doing some wacky gymnastics in the code do you?

It takes a lot to describe what's going on with A because it's a relatively new concept, related to the subject of the article. If partial application would be just as well known as function definition, the explanation could be shorter.

<comment><USER15> But it's not. "partial" is specifically defined as its own function in the article. If you're a developer coming across this guy's project for the first time, you'll have to dig one level further into the stack before you find it.

Only then do you get to learn those unnecessary gymnastics, so you can follow the rest of his code.

<comment><USER17> If "partial" were only usable for this one function you'd be right. But actually it's a very common, general function (under various names); in many languages it's already present in the standard library, or at least in an established library that's commonly used.

Fundamentally this is what programming is all about - finding the commonality that you can pull out and reuse, rather than doing every single case by hand.

</comment></comment></comment></comment></comment></comment><comment><USER18> Point-free isn't going to be worth it in a language where composition and application of functions isn't syntactically and computationally as cheap as possible. It also only works perfectly well for unary functions and situationally for binary functions. Anything more and the contortions start being more unreadable than variables.

In Haskell partial application is literally free and composition is an operator. That's why point-free works well there. No point in importing syntactic ideas that don't make sense in the new context.

Edit: I should also point out there's a subtle corner-case if you mix these transformations with uses of `seq`.

</comment><comment><USER19> So, you get a single point-free function, at the cost of two other point-full functions (at least one of which will utilize run-time introspection)? All you're doing is pushing the "point" somewhere else - in this case down onto Math.max.

Seems like a net loss of simplicity and readability.

<comment><USER20> That's only true if you never reuse Math.max. I believe the philosophy the OP is espousing is to write a few point-ful funcitons when necessary and then make the bulk of your business logic be recombinations of those in a point- free way, rather than redoing the points at every function the whole program wide.

I will agree it is a big loss of readability for someone who's not used to reading programs that are written like that. And that includes me. But I have faith it will get easier to read and eventually I will find it easier to read code written in that style than code written in the old style.

</comment></comment><comment><USER21> I disagree, I don't know may be it's a preference thing but I think the code with points is readable for me and doesn't require to write extra function, class etc.

<comment><USER16> A hallmark example of point-free style in J is likely the example of calculating the average value of the array:

+/ % #

Here literally you say "I'm calculating sum of elements (+/) which then divide (%) into the number of elements (#)".

If your array isn't just a one variable (x) but instead is obtained as a result of an expression, you may not want to repeat that expression twice or to invent an intermediate variable to store the array result. Here point-free style can help.

</comment></comment><comment><USER22> Composition and currying are great if you're the only person working on a project, because you will understand everything that's going on. But my experience has been that developers will not inherently come to the same conclusions with this style, using this on a team of more than two leads to project fragility and incongruousness.

</comment><comment><USER23> A couple of posts that really made this click for me (just beginning my programming career). This is javascript library, but the concepts do translate functional programming in general.

[http://fr.umio.us/why-ramda/](http://fr.umio.us/why-ramda/)

[http://randycoulman.com/blog/2016/05/24/thinking-in-ramda- ge...](http://randycoulman.com/blog/2016/05/24/thinking-in-ramda-getting- started/)

(Read all posts from the second link)

After reading this series of blog posts and using Ramda.js for the last year or so, I can see the value here. But you do have to "get it" which means learning additional concepts, if the additional learning isn't a burden to your team (I hope your team isn't averse to learning new concepts) then I think it's an asset to be used when it makes sense.

</comment><comment><USER24> given the article neglects to provide an example, I have one handy from my git:

[https://gist.github.com/ajeffrey/092b71a3ad5f2034601574262c5...](https://gist.github.com/ajeffrey/092b71a3ad5f2034601574262c5c1167#file- cgol-js-L26)

Note how on this line, arguments are not present and passed around - the input of the function being declared is simply passed from function to function, with the output of each becoming the input of the next. When you can write code this way, it improves clarity by putting the focus on what the code is _doing_ rather than whatever arbitrary name you're giving the input parameters. It's definitely a tool for use in specific situations though as often the name of the arguments helps a great deal with readability.

</comment><comment><USER25> So point-free avoids the parameters. Why not call it parameter-free. Or argument-free.

<comment><USER17> The concept originated in topology, where the parameters to your functions are points (in a space).

</comment></comment><comment><USER26> FWIW I wrote a related blog post:

"Shell Pipelines Support Vectorized, Point-Free, and Imperative Style"

[http://www.oilshell.org/blog/2017/01/15.html](http://www.oilshell.org/blog/2017/01/15.html)

I'm also writing a blog post right now that uses this real instance of point- free style:

        count-nul() {
            od -A n -t x1 | grep -o '00' | wc -l
        }
        # usage: count-nul < myfile.bin

The issue is that grep can't find NUL bytes, because argv is NUL-terminated. So you have to convert to hex, grep, and then count.

</comment><comment><USER27> We use point free notation in JavaScript most often with "fluent" apis like d3 or underscore, where instead of using an explicit compose operator like in haskell, each method in a chain returns the context with a ton of chainable methods defined on it.

The big difference is that Haskell (and other ML-syntax languages) make currying the default so it's much less noisy syntactically to use pointfree style than it is in JavaScript.

[https://wiki.haskell.org/Pointfree](https://wiki.haskell.org/Pointfree)

</comment><comment><USER28> This seems like a great way to obfuscate all your code without any actual benefit. Can anyone give me a real example of how this is somehow a better way to do things? Maybe the application for this is only for a specific field that I'm not familiar with?

<comment><USER10> Working with streams of data often makes pointfree syntax beneficial, since you can start to see the omitted arguments "flow" through your chain of composed functions. If you write

       f # g = \x -> g (f x)

then you could implement functions like this:

       f :: Stream Int -> Stream Int
       f = drop 3       -- drop the first three elements
         # skipEvery 3  -- skip every third element
         # sumEvery 2   -- sum every consecutive pair of elements
         # map (* 2)    -- double everything

This would turn

       1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...

into

       2 * (4 + 5), 2 * (7 + 8), 2 * (10 + 11), ...

You can find many _non-pointfree_ versions of similar streaming code here: [https://github.com/snoyberg/conduit/](https://github.com/snoyberg/conduit/)

</comment></comment><comment><USER29> From a Clojure standpoint, I don't think there's much going on here beyond comparing this:

      (defn at-least-two [x] (max x 2))

with this:

      (def at-least-two (partial max 2))</comment>


3D printing: A third-world dimension  - <AUTHOR> http://www.economist.com/news/science-and-technology/21565577-new-manufacturing-technique-could-help-poor-countries-well-rich-ones <comment><AUTHOR> 3D printing this. 3D printing that. It's quite the buzz, no doubt.

This article made me think about a recent TC article that described the positive impact Airbnb has had / continues to have in communities that do not traditionally have hotels, etc.

Gotta love positive externalities...

Cheers</comment>


Facebook Removes, Then Restores Warren Ads Calling for Breakup - <AUTHOR> https://www.bloomberg.com/news/articles/2019-03-12/facebook-removes-then-restores-warren-ads-calling-for-breakup <comment><USER2> Extensive discussion: [https://news.ycombinator.com/item?id=19363561](https://news.ycombinator.com/item?id=19363561)

</comment><comment><USER3> How about taking away personhood from corporations.</comment>


Overtime and Startups - <AUTHOR> http://jroller.com/page/cardsharp?entry=overtime_and_startups <comment><USER2> If an organization even uses the word "overtime," they're not a startup. In a startup you just take it for granted you're working all the time. You don't have a special word for it.

<comment><USER3> Yup, I can't imagine true startups having specified work hours at all. A company in its startup stage isn't run like a corporation and that's why it's able to be more productive.</comment></comment>


How to finance a startup: Angel/Seed investors, VC, Banking/Debt. - <AUTHOR> http://www.businesshackers.com/2007/06/16/how-to-finance-a-startup-angelseed-investors-vc-bankingdebt/ <comment><USER2> I'd like to read: How to help start-ups get financed, --instead of telling the start-up about the money deal, tell them the exact experience in 12 steps of what hooking up looks like.</comment>


Facebook's Local Market is trouble for P2P marketplaces - <AUTHOR> https://www.techinasia.com/facebooks-local-market-carousell-tokopedias-worst-nightmare/ <comment><USER2> Was only a matter of time before this happened. With the sheer number of users as possible consumers this is guaranteed to last.</comment>


Remacs – A community-driven port of Emacs to Rust - <AUTHOR> https://github.com/Wilfred/remacs <comment><USER2> This looks like a lot of work for what amounts to very little end-user benefit.

Honestly, I'd rather see more progress on the Emacs Lisp/Guile migration than on a putative C/Rust migration. At least with the Guile switch, it's something that will have an obvious (and probably positive) impact on the people that use the editor.

<comment><USER3> It seems a bit rude to complain about volunteers for doing something they want to do rather than what you want them to do.

I imagine that this, for the most part, isn't even opportunity cost: the people working on it probably wouldn't be contributing to full Emacs (or the Guile effort) anyway. _And_ this sort of effort seems like it could be an on- boarding experience to improving the original code base, e.g. in the process of porting, someone notices a bug and so submits a patch upstream, or maybe the repeated exposure to a variety of the internal components gives someone enough familiarity with them to contribute if this project peters out.

<comment><USER4> This is one of the main issues of open source - people tend to work on what seems fun/interesting/rewarding. As a result a lot of people work on different implementations of the same things instead of coming up with one good solution and creating new things on top of it.

Moreover, while some people work on the endless clones of the same thing, the completely different set of contributors is wasting their time cleaning up / fixing bugs / writing documentation for those endless clones.

Examples are: a lot of Linux distros, which are almost the same but not one of them comes even close to MacOSX as a desktop OS. Javascript frameworks which solve the same rendering problem again and again.

On the other side, there is Spacemacs, which tries to provide solid modern IDE-like experience on top of existing Emacs solution, leaving low-level stuff to emacs core team.

<comment><USER5> This is not something intrinsic in the nature of open source, but in how humans like to spend their free time. Of course we could all be much more _efficient_ if we all had the same goals, but that's not how things work in a free society.

<comment><USER4> As a society we win big by collaboration in the first place. Not by diversity, disagreeing with other people and doing everything "your way". Imagine 1000 independent people trying to build Boeing in an opensource way.

<comment><USER6> What a bizarre argument. 1,000+ independent people trying their hand in the business world is ultimately how a Boeing is conceived. That is the extreme diversity that benefits society.

<comment><USER4> I didn't quite get it. Do you mean that Boeing is built by independent people who work on whatever they want to and then a plane is assembled of that independently built parts and no one takes the responsibility of the result? Or you mean that Boeing is subcontracting particular work to independent firms serving as architect, coordinator, and sponsor and taking all the responsibility?

<comment><USER7> > "Do you mean that Boeing is built by independent people who work on whatever > they want to and then a plane is assembled of that independently built parts > and no one takes the responsibility of the result?"

Somewhere in the middle. If you consider the work that goes into making a plane, you'll see quite a bit of it happens outside Boeing. To give one example, think of the aluminium that goes into the plane. Boeing doesn't have to mine bauxite, extract the aluminium from the bauxite, transport it to the place where its sold, etc...

A classic example of this ground-up view was this example given by Milton Friedman about the pencil. I don't agree with many of his views, but I think he makes a good point in this video:

[https://www.youtube.com/watch?v=R5Gppi-O3a8](https://www.youtube.com/watch?v=R5Gppi-O3a8)

EDIT: Turns out the source of this example may have been Leonard Read:

[https://en.wikisource.org/wiki/I,_Pencil](https://en.wikisource.org/wiki/I,_Pencil)

<comment><USER4> Great video, thank you! Yes, most of the work is done outside of Boeing, but it is done specially for Boeing, not that people work on whatever sort of aluminum they want but they know Boeing demand and supply them with the particular sort. On the other side Boeing pay them money so they can proceed to mine. This is what I mean under the collaboration.

<comment><USER7> Glad you liked the video.

Regarding...

"On the other side Boeing pay them money so they can proceed to mine."

Perhaps you know more about Boeing's business arrangements than me, but I certainly couldn't verify that that's true. From what little I know, it's possible Boeing's aluminium suppliers work exclusively for Boeing, but it's also possible these same aluminium suppliers have multiple customers. The point being, there could be people who work as part of the chain to produce and transport the aluminium who have no idea who will eventually use it.

</comment></comment></comment></comment></comment></comment></comment></comment></comment></comment><comment><USER8> Not intended to be a port, but I occasionally search for an editor that is customizable in Python to the extent Emacs is.

Leo Editor ([http://leoeditor.com](http://leoeditor.com)) fits that bill. It's been around since the 90's (and the web page looks like it's from the 90's). Even though it has many users, the documentation is quite poor. If I had another life to live, I would learn it well and improve the docs - and then port over everything I like in Emacs to it.

It is sorely in need of more tutorials. You'll find some in various people's blog posts, but not enough. I know enough of it to know that it is very powerful, but not enough to use it well day-to-day.

<comment><USER9> That looks interesting thanks for sharing. Too bad it wasn't meant to be run on macOS.

<comment><USER8> >Too bad it wasn't meant to be run on macOS.

What makes you say that?

<comment><USER9> It is stated in their _install-readme_ on github.

<comment><USER8> Interesting - I wonder what dependency they could have that prevents its use on MacOS. I run it on both Linux and Windows just fine. At the end of the day, it's just a Python program.

</comment></comment></comment></comment></comment><comment><USER10> Looks promising. First neovim and now this. Even if it doesn't replace emacs (which it won't) if it can enrich the community and maybe bring more users while fixing archaic code bases of hard to find errors and UBs then it is still a win. Neovim pushed vim (Vim suddenly became more active around the time neovim gained traction atleast according to github graphs and releases). So I hope this project brings new life to the core C code in emacs and fixes the problems that it never had (according to the core dev knowledge). When I tried emacs I found it to be a bit like atom at startups. Couldn't get into the keybindings coming from vim.

If it gets faster, I might give it a try again (in evil ofcourse, gotta have them keys).

<comment><USER11> > Even if it doesn't replace emacs (which it won't)

I dunno. First of all it wants to replace the C-bits of Emacs, not all that is in elisp land. Second of all, why not? I do not think the goals of Remacs are overly ambitious: port the C-platform under Emacs's elisp env to Rust. This will probably result in a much healthier and hackable codebase for that functionality, as it: is a more modern language, has less legacy (platforms) to support, is fully open source (Rust) to the level RMS would approve of.

</comment></comment><comment><USER12> Makes sense. Rust is systems language and emacs is an OS replacement.

</comment><comment><USER13> The README doesn't say much about whether and how the rewrite would differ from the main Emacs implementation in terms of threading architecture. A major problem (I think this is an uncontroversial statement) with the main Emacs implementation is that it is fairly common, in every day use, to block the main thread responsible for accepting keyboard input and repainting the screen etc. This (again, uncontroversial I think) is embarrassing in such an otherwise great bit of software in 2017.

<comment><USER14> IMHO, as someone who absolutely loves emacs, this is the biggest problem with emacs.

</comment></comment><comment><USER15> As one poster here said, "Makes sense. Rust is systems language and emacs is an OS replacement."

If anything, it could be great if Rust is used to implement a faster Emacs Lisp compiler/interpreter, thus preserving all of Emacs Lisp source (and saving some implementation time.)

And make it fully multitasking so there are no more problems of key input getting stuck because of a process.

<comment><USER16> Rejoice, if you look at the repository you'll see that's exactly what they're doing - replacing the runtime/C underpinnings and keeping the elisp.

</comment></comment><comment><USER17> Doesn't seem to have a copyright assignment policy in place so this can't be upstreamed as is.

[https://www.gnu.org/software/emacs/manual/html_node/emacs/Co...](https://www.gnu.org/software/emacs/manual/html_node/emacs/Copyright- Assignment.html)

So opened - [https://github.com/Wilfred/remacs/issues/238](https://github.com/Wilfred/remacs/issues/238)

<comment><USER18> I wish one day the FSF would actually show genuine _trust_ in their GPL and ditch their requirement for people to provide copyright assignments.

God knows how many GPLed lines of code has _not_ been submitted back upstream to them over this minor technicality, and that's such a shame.

I honestly think this is for the FSF to fix, not everyone else.

<comment><USER19> As I understand it, it's not lack of trust, but rather that if a legal dispute occurs, it's easier if only one party is the copyright holder rather than having to find hundreds of developers scattered around the world.

<comment><USER18> Let's say that argument is fair enough, at least in theory.

How many legal disputes have the FSF had recently, in practice, where this turned out crucial?

More specifically: how much have they gained by this vs how many contributions have they lost?

Until proven otherwise, I'd expect them to clearly be in the negative here.

<comment> torrent-of-ions So you'll wait until the FSF finds it cannot defend the GPL in court and _then_ try to get copyright assignments from thousands of programmers? That's downright stupid.

If there was evidence that some third party could enforce GPL compliance on behalf of the copyright holder(s) then maybe the FSF should consider relaxing the requirements.

Do you recall that Sony failed to comply with the GPL when they used LAME? The LAME community wasn't prepared to actually do anything about it. That's the kind of shit that would happen, especially to smaller projects. There are big companies out there that want to use your hard work without compensation and shit all over the GPL. Never forget that.

<comment><USER18> > So you'll wait until the FSF finds it cannot defend the GPL in court and > then try to get copyright assignments from thousands of programmers?

I'll rather have code which is harder to defend than defense for code I never got.

</comment></comment></comment></comment></comment></comment><comment><USER17> Some speculatively useful things that could arise out of this -

It may have useful security properties over time. People use emacs for a great deal of arbitrary text reading and transform from things directly from email and the web. Elisp would largely be at fault for security bugs but perhaps this would help in areas.

Perhaps a binding to use Alacritty directly for a port to Wayland? Emacs has a pretty terminal centric view of its UI output as is.

A tight binding to Servo allowing a serious upgrade to Emacs visual and ui capabilities.

<comment><USER20> A while ago, when this project had just started, I filed an issue to discuss the windowing/GUI system: [https://github.com/Wilfred/remacs/issues/25](https://github.com/Wilfred/remacs/issues/25)

</comment></comment><comment><USER21> I guess every language needs an Emacs port to grow up.

None of those ports ever see even the tiniest beginning of something actually usable (heck, even Climacs faltered), but I'm still all for it!

<comment><USER22> One of the cool things about Rust is that it's compatible with C. You can rewrite individual C functions in Rust, compile both halves of the program and link them together into your executable. This lets you incrementally move from a pure-C program to a hybrid, to a pure-Rust program over time.

Also, 90+% of Emacs is written in Lisp which doesn't need to be ported at all.

I just built it, and it's fully functional. It'll be a perfect replacement for Emacs at every stage of the port, which is really cool.

<comment><USER23> The most (debatably) useful thing they could do is write the LISP in Rust. That might have some use. As it is, this project just creates an Emacs with _more_ dependencies.

<comment><USER22> No, rewriting all of that Lisp code would be impossible. It's also unnecessary. The primary advantage of Rust is safety; the compiler ensures that either your code follows the rules or that it doesn't compile at all. The Lisp code, on the other hand, already has that safety. No manipulation of conses or vectors or other lisp data structures will ever result in a buffer overflow, or a null-pointer dereference, or in doubly-freed memory, or any of a hundred other of C's pitfalls. On top of that, Lisp is extremely fast to write, test, and debug.

But of course, that safety only exists to the extent that the Lisp interpreter/compiler is bug-free. The C side of Emacs's code base is alternatively great and terrible, depending on how you look at it. On the one hand, 30 years of continuous development has resulted in code that works really well. On the other hand, it is very hard to extend this code to support new features (good multi-threading support has been in the works for ages, moving to lexical scoping and away from dynamic scoping has taken years, etc). This is because it's difficult to maintain quality and compatibility while also navigating such an old project.

Once enough of this old C code is rewritten in Rust, it'll be possible to leverage the memory safety features (and the type system) of Rust to innovate much more quickly. Not as quickly as you can innovate on code that's written in Lisp, but much more quickly than you can innovate on the C code.

Besides, the number of libraries that a program uses is only a detriment if it means that the developers have to spend more time installing and maintaining those libraries. Cargo makes this comparatively painless.

<comment><USER23> >> No, rewriting all of that Lisp code would be impossible.

I thought the lisp was written in C and so moving it to Rust would be useful. Perhaps it's written in itself. If that's the case then my bad. I did not mean rewrite the list parts in rust.

<comment><USER24> > I thought the lisp was written in C

I am unclear about what you mean by this? The lisp in emacs is elisp ([0] emacs lisp). More generally, Lisp is the name given to a class of languages including Guile, Racket, Scheme, Common Lisp that generally feature homomorphism, functional style and parentheses to enable the homomorphism.

[0] [https://en.wikipedia.org/wiki/Emacs_Lisp](https://en.wikipedia.org/wiki/Emacs_Lisp)

<comment><USER23> I checked wikipedia:

[https://en.wikipedia.org/wiki/Emacs_Lisp](https://en.wikipedia.org/wiki/Emacs_Lisp)

The LISP interpreter is written in C. I was trying to suggest that it may be useful to rewrite that in Rust. That is a necessary step in getting rid of the C dependencies for emacs.

</comment></comment></comment></comment></comment></comment></comment><comment><USER25> What a lovely idea!

Question to the devs: are you planning on redoing how the gui subsystem works, to work with (for example) the gtk event loop? I've always wanted to try to put a qt frontend on emacs, but have always been horrified away by that subsystem.

<comment><USER26> I'd say its unlikely to happen as they are aiming for bug-for-bug compatibility.

</comment></comment><comment><USER27> I'd like to see a community-driven port of Sublime Text to Rust, with a basic ST3 like cross-platform UI, and offering a base set of UI primitives (buttons, panels, dropdown, inline styled annotations, etc) available for plugins, and first class support for JS plugins/extensions.

<comment><USER28> Doesn't port imply having access to the original sourcecode?

There are many unrelated editors that have cloned aspects of Sublime to varying degrees in the 2010s.

<comment><USER27> Well, cleanroom re-implementation if you prefer.

<comment><USER29> Really not worth doing there don't appear to be any unique aspects worth copying and its closed source.

<comment><USER27> It's not about having unique aspects. The Plan 9 editor is unique -- and nobody cares for it.

It's about having useful aspects. And ST3 is the best example we have of a native, fast, modern, cross platform, plugin-able editor.

<comment><USER24> Apart from emacs of course. (depending on what you mean by modern...)

</comment></comment></comment></comment></comment></comment><comment><USER30> How hard would it be for the existing developers of _CEmacs_ to get on board with Rust if they decided to make this the upstream core? I did not study Rust ever but did follow its evolution, and the language seems complicated to me, compared to c.

<comment><USER31> I think only a few bits of emacs are in C. The vast majority of it is elisp.

<comment><USER30> Yes but the maintainers will have to learn Rust for those little bits, no? And those bits are the most complex ones.

</comment></comment></comment><comment><USER32> What we need next is Rim to start the next phase of the holy wars.

<comment><USER33> Like the xi editor? [https://github.com/google/xi- editor](https://github.com/google/xi-editor)

Though I guess it's not actually a port of Vim, just a vi-like editor.

<comment><USER20> Xi isn't vi-like, although it is on their roadmap.

</comment></comment></comment><comment><USER34> This is only topic-adjacent, but here is my number one emacs wish: for the GTK frontend to be pure GTK with no X, so that it can be run directly as a Wayland client.

</comment><comment><USER35> Prediction: within a year this project will be dead due to the lack of uptake. It doesn't solve any real problems and uses a relatively obscure programming language. There's no way it will ever reach anything close to a critical mass.

</comment><comment><USER36> As an avid emacs user... I would never use this.

<comment><USER22> Why not?</comment></comment>