1. 7

    I pretty much always have three books going, so currently:

    • Light reading: Thinking in Bets by Annie Duke. Risk, uncertainty, heuristics. I’m not usually a fan of books with the structure “I did X for 20 years so let me apply that to all of life”, but this one’s hitting the books for its research and I’m a sucker for poker stories.
    • Dense reading: Discourses, Fragments, Handbook by Epictetus, translated by Robin Hard. On a stoicism kick. Reading around the dualism of book one has made this a slog and I may skip ahead or move on to Seneca.
    • Programming: Domain Modeling Made Functional, Scott Wlaschin. Still working on this one.
    1. 2

      That’s exactly my strategy as well (one light, one dense and one technical), I find it means I’m always in the mood to read something (otherwise I’ll resort to shallow articles and silly tutorials online).

      I’ve also found the full Discourses more “diluted” than the Enchiridion and didn’t finish it. I definitely suggest you pick up the latter, if you haven’t already.

      1. 1

        I’m also trying this:

        • One light/escapism: Fool’s Errand - Robin Hobb
        • One heavy: Selected Non-Fictions - Borges
        • One research: The Search for the Perfect Language - Umberto Eco
    1. [Comment removed by author]

      1. 10

        Haskell has no syntax in the core language to sequence one expression after another.

        It has quite a few alternatives actually. Depending what you mean by “syntax in the core language”, there are some things with specific grammar rules in the Haskell98 and Haskell2010 standards; there are some “userspace” functions/operators (i.e. their syntax is a special case of functions/operators) which are nevertheless mandated by those standards; there are some things which the de facto implementation GHC supports (e.g. via commandline flags); etc. Here are a few:

        • a : b is the expression a followed by the sequence of expressions b (all of the same type)
        • a ++ b is the sequence a followed by the sequence b (again, of the same type)
        • [a, b] is a sequence of the expression a followed by the expression b (of the same type)
        • (a, b) is a sequence of the expression a followed by the expression b (can be different types)
        • f . g is the expression g followed by the expression f (input and output types must coincide)
        • g >>> f is the expression g followed by the expression f (same as above but their order flipped)
        • a -< b is the expression b followed by the expression a (must have compatible input/output types)
        • do { a; b } is the expression a followed by the expression b
        • f <$> x is the expression followed by the expression f (must have compatible input/output types)

        These all define a specific order on their sub-expressions. They’re not all identical, but they follow roughly similar usage:

        • a : b tells a Prolog-style interpreter to perform the computation/branch a before trying those in b
        • a ++ b generalises the above to multiple computations (the above is equivalent to [a] ++ b)
        • [a, b] is a specialisation of the above, equivalent to [a] ++ [b]
        • (a, b) generalises [a, b] to allow different types. We can use this to implement a linear sequence (it’s essentially how GHC implements IO). Somewhat surprisingly, and completely separately to anything IO related, it also represents parallel composition
        • f . g is a rather general form of composition
        • g >>> f is the same as above
        • a -< b is is part of arrow notation and desugars to a mixture of sequential and parallel composition (using lambdas, >>>, (a, b), etc.)
        • do { a; b } is a generalisation of b . a, corresponding to join (const b <$> a), which is the most similar form to the ; operator of other languages you refer to: both because it has the same syntax (an infix ; operator) and a similar meaning (generalised composition). This can also be written as a >> b, and is related to a >>= b and a >=> b which are also built-in sequencing syntax, but didn’t seem worth their own entries.
        • f <$> x is generalised application of f to x. That generality also makes it a composition/pipeline operator

        The reason I’ve listed all these isn’t so much to say “look, there are some!”; but more to point out how many different meanings the word “sequence” can have (a list of values, an composition of functions, a temporal ordering on side-effects, etc.); how many different implementations of sequencing we can build; and, most crucially, that they all seem to overlap and interminge (e.g. the blurring of “container of values” with “context for computation”; how we can generalise a single thing like “composition” in multiple ways; how generalising seemingly-separate ideas ends up at the same result; etc.). This tells us that there’s something important lurking here. I don’t think investigating and harnessing this makes someone a wanker.

        1. [Comment removed by author]

          1. 3

            I’m an application programmer at Atlassian. A monad is a critical tool for code reuse in our applications. It’s not about PLT research or even evaluation order.

        2. 5

          Monads only matter for representing sequential execution in extremely constrained languages, like haskell. (Some people believe monads are useful for other things, but I’m not interested in that debate, I’m just talking about where monads are certainly important.)

          This is not true. Monads are critical for code reuse. I’ve used the concept of a monad in many areas, but explicitly and critically in Scala.

          1. [Comment removed by author]

            1. 4

              It’s not true. IO existed in Haskell way before Wadler’s papers on monads.

              1. [Comment removed by author]

                1. 5

                  Still not true. IO has a monad, IO is not about monad, nor the other way around.

                  1. [Comment removed by author]

                    1. 0
                      bindIO :: IO a -> (a -> IO b) -> IO b
                      bindIO (IO m) k = IO (\ s -> case m s of (# new_s, a #) -> unIO (k a) new_s)
                      
                      1. [Comment removed by author]

                        1. 6

                          I’m not being pedantic and your point is not clear. IO can be sequenced, this sequence can be abstracted, code reuse is what is gained from the abstraction. That is the total relationship between IO and monad.

                          1. [Comment removed by author]

                            1. 5

                              Monad is about much more than IO. IO is about much more than monad.

                              Objects and classes have a different relationship.

                              1. [Comment removed by author]

                                1. 2

                                  This is not being pedantic, it is a very critical part of understanding monad and IO. I teach Haskell at work and have successfully corrected this mistake many times.

                          2. 3

                            Suppose there existed a function that reversed a list. A few fruit grocers used this function to reverse a list of oranges. They also sometimes use it to reverse lists of apples. Other things happened with this function also, but we only know of these specific circumstances.

                            Suppose then someone came along and proclaimed, “the reverse function is all about fruit!” then they wrote an article about this new apparent fact. Would you be able to clearly see a categorical error occurring here? What would you say to the article author? Would you reverse a list of list of functions right in front of their face? Or reverse a list of wiggley-woos? What if that person then replied, “you’re just being pedantic”? Where would you take the discussion from here? Would you be the meany person who informs them that they have almost no grasp of the subject matter? It’s quite a bind to be in :)

                            That’s exactly the error being made here (among some others) and it is a very obvious error only to those who have a concrete understanding of what monad means. It’s not pedantic. It’s not “avoiding a debate.” It’s a significant categorical error, and it is very common among beginners. It limits any further understanding so significantly, that it is better to have no knowledge at all. This specific error is also commonly repeated among beginners, as they struggle and aspire to understand the subject, and to the point that it becomes very difficult to stamp out, even for many of those who know the subject well. The ultimate consequence is a net overall lack of progress in understanding, for absolutely everyone.

                            Who wants to contribute to that?

            2. 3

              Haskell has no syntax in the core language to sequence one expression after another.

              Yes it does: do-notation. You can even use semicolons if you don’t like newlines. It’s the syntax to sequence expressions which can be sequenced. You can’t use semicolons to sequence things that can’t be sequenced in other languages, either.

              And why talk about Maybe but not MonadPlus, free monads, transformers…? All you know is Maybe and IO? Of course it’s boring to you. In stead of writing blog sized posts about how blog tutorials don’t teach you everything you could read up, but oh well you do you.

              1. 3

                By changing each stage to take and return a fat outer type holding the entire context, you can just as easily achieve the cool pipeline effect by defining >>= as function composition rather than bind.

                With bind you don’t have to change each stage.

                Understanding how to write programs which allow change without triggering catastrophic rewrites is pretty useful.

                Understanding why some programs are easy to modify is pretty useful.

                Having language to discuss why some programs are easy to modify and others are not; also pretty useful.

                The original post is about how thinking in terms of Monads can make a program which is hard to modify into a program which is easy to modify, it’s a useful post.

                1. 3

                  Some people believe monads are useful for other things, but I’m not interested in that debate, I’m just talking about where monads are certainly important.

                  First of all, by far the most popular monadic interface in modern software development is not Haskell’s IO type, it’s JavaScript’s Promise type, together with similar systems for writing asynchronous logic in other languages. If we’re talking about use cases where monads are “certainly important,” I think it’s worth mentioning the large number of programmers writing monadic code on a daily basis in languages which certainly do not lack native support for semicolons.

                  I love monads and find they’re actually among the most useful and important tools I’ve ever acquired as a programmer, but I agree that the PLT and functional programming communities could do a better job communicating exactly why monads are actually important. The use of monads as “extendable semicolons” does have some narrow but critically important use cases, such as asynchronous code, exception handling, and recursive backtracking, but I actually believe that the exotic forms of control flow you can express with monads is of only secondary importance.

                  In my experience, the most important consequence of modeling side effects with monads is that it allows you to reliably distinguish between pure and impure functions. The features which you claim make Haskell “extremely constrained” in fact give it an entirely new dimension of expressive power, because whereas most languages only have one form of function, which is implicitly allowed to perform side effects, Haskell has two forms of functions: functions which may perform side effects, and functions which may not. Given that a function’s interaction with an environment is an extremely important aspect of its semantics, this is information that you would be informally documenting and keeping track of anyway; Haskell just allows you document it in a precise, machine-checked format with great integration with the compiler.

                  This immediately allows you to separate functions which perform IO from those which do not, but that’s not actually the coolest part. The coolest part is that once you start defining your own monad types, you can express much much more precise and interesting classes of side effects, like “a function that interacts only with a random number generator” or “a function that interacts only with my database state” or “a function which interacts only with a sequential-identifier generator.” This is the real power of monads: the ability to make fine-grained guarantees about the data dependencies and side effects of a function given only its type signature.

                  1. 6

                    In my experience, the most important consequence of modeling side effects with monads is that it allows you to reliably distinguish between pure and impure functions. The features which you claim make Haskell “extremely constrained” in fact give it an entirely new dimension of expressive power, because whereas most languages only have one form of function, which is implicitly allowed to perform side effects, Haskell has two forms of functions: functions which may perform side effects, and functions which may not.

                    One nit here: the idea of separating pure and effectful operations is actually pretty old. You see this in Pascal and Ada and the like, where “functions” are pure and “procedures” are effectful. This is baked into the core language semantics. The different terms fell out of favor when C/C++ got big, and now people don’t really distinguish them anymore. But there’s no reason we couldn’t start doing that again, aside from inertia and stuff.

                    To my understanding you also don’t need monads to separate effects in pure FP, either; Eff has first-class syntax for effect handlers and takes measures to distinguish the theory from monads.

                    1. 1

                      To my understanding you also don’t need monads to separate effects in pure FP, either;

                      Well, you need something. Proposing to have an effect system without monads is like proposing to do security without passwords: there are some interesting possibilities there, but you have to explain how you’re going to solve the problems that monads solve.

                      Your Eff paper refers to papers on effect tensors to justify the claim that effects can be easier to combine than monads, but then doesn’t seem to actually model those tensors? Their example of what combining effects looks like in practice seems to end in just letting them be composed in the same order that the primary code is composed, when the whole point of a pure language is to be able to get away from that. So while the language is pure at the level of individual effects, it seems to be effectively impure in terms of how composition of effects behaves?

                    2. [Comment removed by author]

                      1. 4

                        It’s not specific to Javascript. The type Task<T> is the same interface in C#, Future<V> is the same thing in Java. The concept is generally useful in all languages. It’s useful even in Haskell, where Async allows the introduction of explicit concurrency, even though the runtime automatically does the work that Task<T> is mostly for in C# (avoiding blocking on threads).

                        In addition async/await is a monadic syntax, which is generally useful (as evidenced by it now being in C#, F#, Scala, Javascript, Python, and soon C++).

                        (LINQ in C# is another generally-useful monadic syntax, which is used for just about everything except doing IO and sequencing.)

                  1. 7

                    Learn Powershell (Register-ScheduledJob,Get-NetAdapter,Get-NetIPAddress,Invoke-WebRequest,Invoke-RestMethod,Export-Csv,Export-Clixml,Out-GridView…)

                    Install PSReadLine for readline-like keybindings. https://github.com/lzybkr/PSReadLine

                    You can use clink https://mridgers.github.io/clink/ for readline bindings on the vanilla cmd.

                    Install ripgrep and fzf.

                    Use pageant with putty. Also, I suppose this is common knowlege, but you can launch putty from the command line while overriding some parameters of a saved session, like ‘putty -load “Some Session” 178.128.78.50’

                    AutoHotkey can be pretty useful.

                    PuTTY seems to not be willing to scroll more than a screenful or two of text

                    Does changing the Lines of scrollback in Configuration > Window help things?

                    In particular, the mouse cursor goes invisible,

                    Is “hide mouse cursor when typing” enabled in Configuration > Window > Appearance?

                    1. 9

                      Yeah, “don’t fight the platform” would be my advice. You have to relax and let the Windowsness of it all wash over you. Admittedly, I also find this impossibly difficult, but trying to turn Windows into a poor clone of a poor clone of a poor clone of AT&T Unix is probably a losing strategy in the intermediate run.

                      1. 2

                        I think you get PSReadLine by default recently, as well as ssh (if you enable it).

                      1. 2

                        If the strings are immutable, why is string.Split(”,”) allocating so much? In Go for example it would returns a slice to the original strict which is only a few bytes long.

                        1. 5

                          System.String.Split predates Span et al; so it likely is allocating new strings instead.

                          1. 3

                            Thanks, I don’t know anything about the .NET environment.

                            It seems like OP could have used a version of Split that returns an array of Span and he would have gotten 90% close while keeping the code straight-forward.

                          2. 3

                            .NET never shares substrings like Java or Go (the object layout is “Pascal-style”, length followed by content, so you can’t do this without changing representations)

                          1. 13

                            The perception of rampant deletionism didn’t do any favors for a lot of folks who had, say, put a lot of effort into covering works of literature, film, anime, manga, or other things like obscure historical events this probably had a chilling effect.

                            Additionally, community just seemed really…I don’t know…silly and officious. The amount of seriousness which Wiki folk seemed to take themselves, coupled with occasionally blatant lack of expertise in editing or moderating, say, technical articles, might make one think twice before contributing.

                            1. 10

                              Well, Wikipedia is a serious business. It is the foremost social verifier of our time. (If you don’t want to click, I quoted the relevant passage below.)

                              So: a social verifier is an institution, authority, Web 2.0 server, etc, etc, which collects and distributes information that its users trust. Wikipedia is a social verifier. So is the Catholic Church. So is the New York Times. So is UC Berkeley… So is the scientific peer-review system. And so on.

                              1. 3

                                Deletionism killed it for me, I don’t bother any more except to fix small mistakes.

                                1. 2

                                  I was never a contributor but the deletionism thing would make me think twice for sure. Not about if my content was notable but about if I wanted to help Wikipedia.

                                1. 4

                                  Web GUI technology has completely surpassed the desktop GUI technology.

                                  Back in the day web stuff was so basic that a desktop GUI was nicer and an upgrade, now that has reversed.

                                  1. 10

                                    I agree to some extent, except that Electron apps (and some web apps) are all but unusable on low-end/older hardware. Many (but not all) are severely lacking in keyboard control and other things that one might expect, too. Every Electron app seems to be oblivious to multilingual users and underlines every word, despite me switching input methods.

                                    1. 2

                                      I’d like a HTML-based GUI that doesn’t embed a full renderer like Electron does – something that maps HTML onto native controls (including accessibility stuff) could be really neat.

                                      1. 1

                                        Isn’t that what React Native is? Maybe that’ll be the hot new thing instead of Electron; would prolly be an upgrade.

                                        Edit: whoops, it’s iOS and Android only.

                                        1. 1

                                          React Native is just running your app as JS and communicating to a native set of widgets and layout, which need to be implemented per platform. If desktop support were something FB had as a priority it’d be a good option for a lot of people, but… it’s not.

                                    2. 9

                                      Couldn’t disagree more, and the reason is accessibility. it’s super trivial for desktop app developers to add keyboard shortcuts and other accessibility aids to their apps. Web developers, despite the fact that these standards like ARIA exist, seem unwilling to adopt them in any sizable number.

                                      We can have this conversation again when the Hello World app produced by your average Java framework is Aria accessible, has keyboard shortcuts for everything, and works properly with screen readers.

                                      1. 4

                                        If the developer doesn’t care it doesn’t matter if it’s a desktop app or a web app. They wont do it either way.

                                        The difficulty of adding keyboard shortcuts or adding accessibility tags is not dramatically different and quite easy for web apps too.

                                      2. 3

                                        As bad as GUI toolkits are, web tech is a lot more awkward to make GUIs with than any major cross-platform toolkit, simply because it’s a hack to draw anything with the DOM. (You’re literally live-editing the AST of a rich text document. It’s amazing that it works at all.)

                                        1. 1

                                          Your sole argument about DOM being a hack and akward is it being live-editing an AST? If anything, this might be a pro of the DOM API… I don’t see how a technology widely used, having API clearly defined for those use cases and supported by modern and old browsers can be called a hack and akwards. Meanwhile you have your average GUI toolkit that still ask you to design your AST in the code, put the styling right beside the event handling and often introduce first how to put a button a X,Y because using container and layout is akward and complicated.

                                          1. 1

                                            A regular GUI toolkit doesn’t involve manipulating the AST of a markup language. It involves manipulating containers that map conceptually to layout, using already-implemented widgets. There’s an event handling system designed to efficiently handle widget-specific mappings, focus changes, and other common situations, as well as having sane defaults (versus having an event system that needed to be tacked on ten years after the other features were written).

                                            The act of spawning a widget in a web app is an ugly hack, simply because document markup structurally conflicts with GUI layout in ways that the web developer must bodge.

                                            If any GUI toolkit requires you to jump through hoops to draw a dot on the screen, it’s broken. (By this standard, most popular GUI toolkits are also broken, but HTML is the most broken of all.)

                                            1. 1

                                              Yeah, regular GUI toolkit doesn’t involve AST and markup language, such as HTML, XAML, Android, QML, etc. In my opinion, working on a human readable and understandable AST might be the key of the web plateform GUI? Drawing anything is as simple as adding a node or subtree to my current tree. It’s as simple to do by hand than programmaticaly. If anything go wrong I have well made developpers tool to see and live edit this tree. Call it a hack all you want, I call it a successful low-level reprensentation to share the GUI state to the renderer, much better and powerfull than what you can do with Tcl or xlib (Although, much more heavy).

                                              If any GUI toolkit requires you to jump through hoops to draw a dot on the screen, it’s broken. (By this standard, most popular GUI toolkits are also broken, but HTML is the most broken of all.)

                                              There you go: <html><head></head><body>.</body></html>. By this test we can now assert that HTML is not broken (Or at least just as much as the others).

                                              1. 2

                                                You haven’t drawn a dot. You’ve typeset a period, and spent 40 characters doing it. And, typesetting text is what HTML is for, so it’s what it’s best at. If you actually want to ensure the period resembles a dot, set its x,y position, and set its color, you’ll need hundreds more characters.

                                                In BASIC, you can just do pset(x, y, color)

                                                In TK: canvas .c ; .c create point x y color ; pack .c

                                                An AST only makes sense if you are actually parsing or generating a structured language. The structure of an HTML document doesn’t coincide with the structure of a PARC GUI (i.e., every major GUI app since 1977), and is an even worse match for the scope of all possible useful GUIs (most of which resemble neither paper nor forms). The reason is that HTML was only ever intended to display minimally-formatted rich text.

                                                “Drawing something” is usually easier than manipulating the DOM. “Drawing something” is only trivial on the DOM when what you’re drawing is structured like a text document.

                                      1. 11

                                        Lots of other things to comment on but I firmly think interfaces-as-structural-types is one of Go’s greatest strengths. To put it in the “bad” group is upsetting and kind of made me discount the rest of the article.

                                        1. 6

                                          I think it’s a philosophical difference:

                                          Some developers write code to document to coworkers what they are doing, and some developers just want things to compile with the least effort possible.

                                          1. 2

                                            It’s not a matter of least effort. Go is one of the first languages I know of that was primarily designed for large teams of engineers instead of individuals. Heavy focus on compile time, gofmt, not allowing compilation with unused variables, etc, all directly stem from this approach. Structural typing specifically reduces cross-team friction. Go will often make decisions that incur individual overhead to reduce overall team overhead

                                            1. 5

                                              Not sure I agree on this.

                                              Compilation is not particularly fast, even compared to more more modern languages like Algol, its dependency management is a disaster, it’s error handling ignores the last few decades of lessons learned and the amount of code duplication it forces upon developers makes it hard to maintain.

                                              I think it does well in terms of helping Google’s requirements of having all code in a large mono-repo, and enabling people who have no practical experience to produce code.

                                              1. 2

                                                Whether or not they succeeded at being fast wasn’t my point (though my position is they did succeed). My point is the kinds of things they emphasized in language design. Russ Cox argues that compilation speed is one of the reasons they don’t have generics, for instance.

                                                Dependency management doesn’t matter with large teams in a mono repo, yeah, and the code duplication to me felt like it would be an enormous issue when I started but in practice, half a million lines of code later, it doesn’t come up nearly as much as you’d think.

                                                1. 2

                                                  Compilation doesn’t have to be slow just because generics are involved, the author of D demonstrated that fairly well. I think this is rather an issue of generics not having been invented at Bell Labs (or decent error handling in this regard).

                                                  I’m not sure why “dependency management doesn’t matter if you are a Google employee” should be a convincing argument for programming-in-the-large for the millions of non-Googlers out there.

                                              2. 2

                                                Structural typing specifically reduces cross-team friction.

                                                Can you talk about how structural typing accomplishes this?

                                                EDIT: Ah, I see you answered this in another thread.

                                            2. 3

                                              Languages are funny. I’d consider defer to be a bad idea elevated to a language feature, but it’s in the “good” group 😀

                                              1. 2

                                                Can you explain why you like this idea?

                                                1. 4

                                                  Sure! Let’s say someone writes a library you use frequently but writes it such that it does not explicitly implement any interfaces, as the author of the above post prefers. Maybe you use a library called Beep like this:

                                                  type Beeper1 struct { ... }
                                                  func NewBeeper1() *Beeper1 { ... }
                                                  func (b *Beeper1) Beep() { ... }
                                                  

                                                  You are writing your library, but want to support multiple implementations of Beepers. Maybe there’s another beeper (for a test, or another library, or something else) that also has a Beep() method. So you write your code to just expect

                                                  type Beeper interface {
                                                    Beep()
                                                  }
                                                  

                                                  Now you can use the third party code, your code, your test code, etc, without having to change the third party code upstream to implement your interface.

                                                  This is a super contrived example, but as your codebase and team grows larger, this becomes incredibly useful for reducing friction in having teams of engineers work together with minimal stepping on each other’s toes.

                                                  Ultimately, I describe Go’s structural typing system to Python programmers like the static typing equivalent of Python’s “duck typing” principle, which is, if it looks like a duck and quacks like a duck, just treat it like a duck. Coming from statically typed languages that require you to list what interfaces a concrete instance implement, Go not requiring that dance felt like a huge reduction in friction to me.

                                                  1. 2

                                                    I guess, to me, it feels like a strictly worse approach than what Rust has with traits, or Haskell with typeclasses, because there’s no explicit guarantee that a “Beeper” is actually abiding by the contract of the “Beeper” interface. It could have a “Beep” method that actually nukes Vienna. There’s friction to implementing a trait or typeclass for a new type, but there’s also value in it. If I have explicitly implemented a trait, there’s documentation of the type’s usage in that context as well as of its conformance with the interface.

                                                    1. 3

                                                      A frequent pattern in Go to get some of that functionality if you want it is to write something like

                                                      var _ InterfaceName = (*ConcreteType)(nil)
                                                      

                                                      which simply adds a compile time assertion that ConcreteType does indeed implement InterfaceName

                                                      Certainly does nothing to constrain the behavior, but I’m super happy with that (optional) middle ground

                                                      1. 3

                                                        There exists a spectrum: let’s say that on one extreme, it’s maximum programmer friction with minimum risk of mis-use; and on the other extreme, minimum programmer friction with maximum risk of mis-use. Go puts a marker down closer to the latter extreme, judging friction to be a worse evil than risk for their context, and providing some affordances (like the one-liner jtolds mentions) to mitigate some of those risks via convention.

                                                        I think no position on the spectrum is “strictly worse” than any other. It is a question of trade-offs to satisfy particular programming contexts or requirements. I think this is the same for any technical decisionmaking.

                                                        Go makes a lot of decisions this way. (Not all, and there are warts for sure — but many.) I think it is a nice and refreshing change from where most languages (like Rust) decide to land, and I think Go’s success in the market proves it is a viable, or maybe even preferable, compromise-point for many users.

                                                1. 46

                                                  Your original 600MB tracking pixel was a pain in the ass, I’m sure everybody who accessed the website using limited cellular data is very fond of you.

                                                  1. 17

                                                    Especially during a weekend where many (at least Europeans) were traveling.

                                                    Also keep in mind that a lot of people outside the US (i.e. Europe) are on 500MB/1GB plans, so you burn through that with LTE very quickly. And not everyone can afford to buy extra data. This is extremely rude.

                                                    1. 4

                                                      Well even as an american, I accessed this site and that page over my cellular connection. Sigh, even with a 10GiB monthly cap that probably used up a ton of data pointlessly.

                                                    2. 3

                                                      … I was wondering how I ran out of data 4 days early this month.

                                                      1. 4

                                                        I, well, believe it should count as part of the experience.

                                                        1. 2

                                                          You don’t RSS your mobile feeds?

                                                          1. 3

                                                            I haven’t used RSS since like 2009.

                                                            1. 6

                                                              But it was 2002, atleast according to the post dates!

                                                              1. 4

                                                                We have https, too, to make sure those dates weren’t tampered with in transit.

                                                        1. 9

                                                          I liked this line from Engineer’s Obligations:

                                                          When needed, my skill and knowledge shall be given without reservation for the public good.

                                                          1. 4

                                                            That has a really ominous parsing for me.

                                                            I assume the intention is something like:

                                                            When needed, my skill and knowledge shall be given for the public good, without reservation.

                                                            1. 3

                                                              And paid Id add. The markets and taxpayers that cover lots of unnecessary stuff should be able to cover these people working for the public good.

                                                            1. 10

                                                              Some aspects remind me of the ACM’s Code of Ethics.

                                                              1. 7

                                                                This is currently being updated. Here’s the latest draft (3). Here’s the diff for Draft 1 from the 1992 version.

                                                                1. 6

                                                                  I can take the ACM version more seriously, since it presumably entails some means of enforcing this contract. Without that, this is just… well, a nice expression of good intentions. But, ACM membership isn’t much of a requirement for practicing as a “computing professional”, nowadays.

                                                                  When being kicked out of the ACM for violating their Code means that your career is effectively over, then we’ll be on par with the other engineering disciplines – doctors and lawyers aside. I think we’ll get there eventually, but it may take quite some time. The professionalization of civil engineering, for example, took many decades of collapsing bridges and the like.

                                                                  If you’re serious about any of this, go study some history.

                                                                  1. 4

                                                                    To really be enforceable it’d need more than ACM being able to kick individual computing professionals out; it’d also need ways to effectively enforce it against the employers of computing professionals, who are often ultimately the ones asking employees to do unethical things (there are also “rogue” unethical acts, but I don’t think it’s the biggest part of them). In legally regulated areas of engineering that’s done with laws that make it very bad for employers to pressure or retaliate against engineers doing certain kinds of work. If you’re fired for refusing to do something that violates civil-engineering ethics, you can sue, and the company can also be subject to fines/sanctions. I don’t see a near-term mechanism where someone at Google or Amazon can say “no” to a manager’s request, citing a professional code of ethics, and be legally backed up in doing so, which is what would be needed to give it teeth.

                                                                    1. 1

                                                                      When being kicked out of the ACM for violating their Code means that your career is effectively over… I think we’ll get there eventually, but it may take quite some time

                                                                      Wait, hold up; are you saying that this would be a good thing?

                                                                      1. 9

                                                                        If we’re going to use terms like “good” or “bad” here, it would help to qualify for whom. To the point, “bad” for practitioners who expect to make high wages with little or no formal training, accreditation, or personal responsibility for the consequences of their mistakes (honest or otherwise) may well be “good” for the general public. It can get pretty complicated, especially once you start considering the employers of engineers as ethical agents too.

                                                                        Yes, I’m in generally in favor of professionalization, but I’m not exactly holding my breath. I think it will happen inevitably, if slowly, as a consequence of our field maturing and society realizing how potentially dangerous our work really is.

                                                                        1. 2

                                                                          I agree with your analysis in theory, but I have a near 100 kg objection: me.

                                                                          I have no formal training. I’m completely self-taught. And I really feel this as limit and as a pain.
                                                                          But I’ve found several accademics and formally trained developers with very weak understanding of their own field.

                                                                          My recent article about AI misconceptions was born after a debate with an AI researcher and professor.

                                                                          And in my professional work, it happens even more frequently. I can honestly say that I often meet very incompetent people with both high technical responsibilities and high accreditations from University. And I can also honestly say that several very skilled developers I know, are self-taught geeks.

                                                                          How this can happen?

                                                                          My opinion is that our profession is still at its infancy.
                                                                          The fact that we are afraid of an strongly ethical Oath is a proof of this.

                                                                          But also, our profession has little entry barrier: everyone can learn how to program if she has a computer.

                                                                          We should not be afraid of this and create artificial barriers to entry the profession.
                                                                          We should find something better. What? I do not really know.

                                                                          But given my experience, I’m not sure that an artificial barrier to entry would benefit humanity.

                                                                          At least not yet.

                                                                          1. 1

                                                                            I hope I didn’t give the impression of being in favor of premature professionalization! I completely agree, the field of computing is still too young to have a really meaningful and enforceable code of ethics, because we can’t yet ground such a code in a strong consensus about normative practice. All the talk of “best practices” mostly goes to show this lack of agreement. Even a brief comparison with, say, the International Building Code shows how weak these norms are.

                                                                            When there is broad and stable academic consensus about safe and unsafe practices in computing, then perhaps a generation later we’ll be able to hold practitioners to a standard of professional conduct. Again, there is a rich history of this kind of thing in the other engineering professions. The details will depend on historical circumstance, but the general trend is pretty clear I think.

                                                                            But, even then, I don’t see that having a brighter line between amateur and professional programmer should necessarily discourage amateurs. For example, the aircraft manufacturing industry in the US is very highly regulated. But amateurs can build non-commercial aircraft for their own use without being held to any engineering quality standards at all. The risk in home-build aircraft is mostly assumed by the builder-pilot, rather than the public.

                                                                            1. 1

                                                                              I’m pretty sure that one generation is not enough. One generation has already gone, indeed.

                                                                              and… just to be clear… I’m not an “amateur”! I’m a self-taught and professional programmer. ;-)

                                                                        2. 5

                                                                          This is essentially what happens in civil engineering, and to a lesser (but still extant) extent in mechanical engineering. I don’t have anything but anecdotal evidence to support that being a good thing, but I and other people I know who work in mechanical design generally support it. Having the stakes be that high for corner-cutting means that a professional engineer’s sign-off on something really carries weight.

                                                                          1. 5

                                                                            That’s how it works for doctors here in Australia; if you commit major malpractice you can be deregistered, meaning you can no longer practice medicine.

                                                                            None of the doctors I know have expressed any concern about this (the last case I heard of was a gynecologist deregistered for sexually abusing dozens of patients).

                                                                      1. 1

                                                                        I’m not an algorithms guy, nor do I claim to be. This paper(? or whatever you want to call it) makes some interesting claims

                                                                        • Sorting in O(n) time sequentially, O(log(n)) in parallel. That’s fast!
                                                                        • “O(m) space where m is the size of: { min, min + 1, min + 2, … , max - 1, max }”
                                                                          • Directly quoted from the article - I’m not entirely sure what it means for the size to be a set of numbers
                                                                        • “…both the parallel and sequential versions sort the array A with a collision probability of 0.00001%.”
                                                                          • This may seem small, but it’s 1/100000. That’s pretty significant if you’re sorting, say, a million items. Correct me if I’m wrong, but I think that means there’s actually a 10 to 1 chance that there isn’t a collision with a million item sort.

                                                                        Also, doing a ctrl-F for “stable” does result in anything - so there’s no indication if it’s stable or not. I’d lean on the assumption that it’s unstable, because it’s being done in parallel? But again, I’m not an algorithms guy.

                                                                        1. 4

                                                                          Well… it depends on what you mean by stable. All the numbers in the result will be, by definition, in order… but you may get new numbers that weren’t in the original array. The rate of those new numbers is 1/100000.

                                                                          From what I can tell, this is kinda how the algorithm works:

                                                                              lookup = bloomfilter(numbers)
                                                                              for i in range(min(numbers), max(numbers)):
                                                                                  if i in lookup:
                                                                                      yield i
                                                                          

                                                                          It’s important to note that because we’re using bloom filters, this scheme does not handle arrays with duplicate entries. That’s why they mention “unique elements” in the first statement.

                                                                          It’s the i in lookup that has the 0.00001% error rate. The O(m) comes from the fact that we do range(min(numbers), max(numbers))…. the author has a really strange way of stating that with that set notation.

                                                                          One last thing to note is that the rate of O(log(n)) is theoretical assuming an infinite number of available cores.

                                                                          1. 1

                                                                            From what I can tell, this is kinda how the algorithm works:

                                                                            Thanks for this. I skimmed through the whole thing several times without figuring out what the actual algorithm was.

                                                                          2. 3

                                                                            As this is a probabilistic sorting algorithm, you will have a small chance that the given array is not sorted after executing the algorithm. Therefore this algorithm shouldn’t be used critical operations.

                                                                            I’m going to guess it isn’t stable :P

                                                                            1. 1

                                                                              Hah, I definitely missed that. Out of curiosity, do you (or anyone else) know some possible applications of a “mostly sorting” algorithm?

                                                                              1. 4

                                                                                In data science for instance, you rarely need any operation to be perfect, since everything you calculate is statistical anyway.

                                                                                1. 3

                                                                                  This is particularly useful for approximate percentiles. If I have a dataset with 239487283572934 numbers, it’d be hard to store in memory and constantly keep updated and sorted. With this algorithm, all you need to do is maintain: the bloom filter (which is small), the min/max numbers and the total number of records that have been added. This scheme would have O(1) insertion and O(M) query where M is the size of the interval the data is on (ie: how spread apart the min and max are).

                                                                                  The thing that makes this kinda useless though is that it only supports sorting unique items.

                                                                                  1. 2

                                                                                    Perhaps as a first pass, ahead of a proper sorting algorithm?

                                                                              1. 2

                                                                                Wow this is very exciting. If I understand correctly this effectively completes the Lambda Cube for F#.

                                                                                1. 4

                                                                                  It doesn’t. This has very little to do with dependent types, but are more like smart constructors.

                                                                                  Here’s a translation into boring old C# to show that nothing particularly F#-specific (or very fancy at all, given C#‘s type system) is going on at the type level. (I’ve switched out Option for simple nullability.)

                                                                                  1. 1

                                                                                    Actually they are by definition dependent types. They maybe aren’t the most sophisticated dependent types…. but

                                                                                    “A function whose type of return value varies with its argument is a dependent function and the type of this function is called dependent product type, pi-type or simply dependent type.” In your case using a null there doesn’t satisfy this definition because null is a value included in your type. I however do agree, this can be implemented in all kinds of languages and yes is basically a constructor that throws one type if it’s valid and another type if it is not. What matters mostly is how cumbersome the syntax is and that you can have a type for a parameter that always meets your condition there. You KNOW that because it is a PositiveInt, it has a value, and that that value is greater than 0 and you do not need to check for it within the function that takes a PositiveInt as an argument. The type depends on the value.

                                                                                    1. 1

                                                                                      In your case using a null there doesn’t satisfy this definition because null is a value included in your type.

                                                                                      Some and None are both values of the type Option. You can similarly translate this into C# but I couldn’t be bothered (at the CLR level the F# values translate into sub-classes of the Option type).

                                                                                      Dependent types don’t end up in a language by accident, they’re something that has to be explicitly designed in.

                                                                                1. 1

                                                                                  I’m noticing that the Das 3 and the Filco Majestouch both link to the “clicky” MX blue variant. Could this be why they placed higher than other MX counterparts? It would be nice to see switch data.

                                                                                  1. 1

                                                                                    Aren’t the latencies due to electronics and not mechanical? That would explain why the ErgoDox places so average - because the firmware can do so many different things.

                                                                                    1. 1

                                                                                      The latency measurements are the time from when the key starts moving to the time when the USB packet associated with the key makes it out onto the USB bus.

                                                                                      The Apple keyboard in first place has very little key travel, while the Planck and Ergodox in the middle of the heap probably have full-travel mechanical switches that have a way to move before closing the circuit.

                                                                                      1. 2

                                                                                        Which also makes the results surprising because the Filco has quite a lot of key travel. It would be interesting if this could be broken down further by activating the switches directly and comparing that to the overall time.

                                                                                  1. 23

                                                                                    I suggest this story change its name from “made me change” to “asked me to change”.

                                                                                    1. [Comment removed by author]

                                                                                      1. 1

                                                                                        If Stallman had power over them I would see it falling into that fuzzy area, or right through it into a requirement.

                                                                                      2. 1

                                                                                        “Seeing X made me realize Y” is something we hear sometimes. “Made me” is not necessarily about a person forcing a person to do something.

                                                                                        But maybe the headline would have been less ambiguous (and less sensationalist) had it said “A note from rms made me …” or even more removed, “Reading a note from …”, or just “rms’s views made me …”.

                                                                                        1. 1

                                                                                          It’s “made me change” in the sense of “convinced me to change”, not “forced me to”. (I’m suggesting the original title back…)

                                                                                        1. 2

                                                                                          I guess someone has to link to the (legendary) Stack Overflow response about parsing HTML with regex: https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags

                                                                                          1. 2

                                                                                            Also

                                                                                            Some people, when confronted with a problem, think “I know, I’ll use regular expressions.” Now they have two problems.

                                                                                            • Jamie Zawinski
                                                                                            1. 5

                                                                                              I’ve always hated this quote (and most of its variations). As far as I can tell it boils down to “never use regular expressions”, which is bad advice.

                                                                                              1. 3

                                                                                                Indeed, I’d rather see someone use a regex where appropriate than try to re-invent a capture-group from scratch.

                                                                                                EDIT: I’ve seen some absolutely miserable text-extraction code written by juniors who hadn’t ever picked up regex because it was “icky”. A quick demonstration later the whole module could be deleted and replaced with a handful of neat little regexes, making good use of capture groups.

                                                                                                1. 1

                                                                                                  I ‘grew up’ with Perl, so in that day and age, it was something that did need to be said…

                                                                                              1. 2

                                                                                                You can make life a little simpler by:

                                                                                                • using the liftF and iterM functions provided by free
                                                                                                • adding some typedefs for the composed Free type
                                                                                                • (here I also made the parameter on IOOp symmetric so you read the same as what you write)
                                                                                                {-# LANGUAGE DeriveFunctor, LambdaCase #-}
                                                                                                
                                                                                                import Control.Monad.Free (Free (..), iterM, liftF)
                                                                                                
                                                                                                main :: IO ()
                                                                                                main = runOp exampleProgram
                                                                                                
                                                                                                exampleProgram :: SimpleIOProg ()
                                                                                                exampleProgram = do
                                                                                                  write "enter 'quit' to exit\n"
                                                                                                  inputLoop
                                                                                                
                                                                                                inputLoop :: SimpleIOProg ()
                                                                                                inputLoop = do
                                                                                                  write "enter some words> "
                                                                                                  text <- inputLn
                                                                                                  if text == "quit" then end
                                                                                                    else do
                                                                                                    let nWords = length $ words text
                                                                                                    write $ "you entered: " ++ show nWords ++ "\n"
                                                                                                    inputLoop
                                                                                                
                                                                                                write :: a -> IOProg a ()
                                                                                                write s = liftF (Write s ())
                                                                                                
                                                                                                inputLn :: IOProg a a
                                                                                                inputLn = liftF (Read id)
                                                                                                
                                                                                                end :: IOProg a ()
                                                                                                end = liftF EndProgram
                                                                                                
                                                                                                data IOOp a next
                                                                                                  = EndProgram
                                                                                                  | Read (a -> next)
                                                                                                  | Write a next
                                                                                                  deriving (Functor)
                                                                                                
                                                                                                type IOProg a = Free (IOOp a)
                                                                                                type SimpleIOProg = IOProg String
                                                                                                
                                                                                                runOp :: SimpleIOProg () -> IO ()
                                                                                                runOp = iterM interpret
                                                                                                  where
                                                                                                  interpret = \case
                                                                                                    EndProgram -> return ()
                                                                                                    Read next -> getLine >>= next
                                                                                                    Write s next -> putStr s >> next
                                                                                                
                                                                                                1. 4

                                                                                                  I strongly agree with this list, though I’d note that Kotlin is doing a really good job bringing literally all of these except value types to the JVM, in a fairly lightweight syntax to boot. (Properly doing value types would require changes to the JVM, though Kotlin immutable data classes by default behave similarly to value types, which can be useful in many situations.)

                                                                                                  That said, this post inadvertently reminded me of how few people actually get what C# async and await do. If you look at the sample output in that example, the astute reader will realize that nothing happens in the asynchronous task until .Wait() is called. That’s because Main() is not itself an asynchronous method, and the author never set up a task runner, so this program actually runs entirely in a single thread. I suspect what the author wanted to do would be closer to something like

                                                                                                  TaskAwaiter david = Task.Run(ThinkAboutIt).GetAwaiter();
                                                                                                  ...
                                                                                                  int result = david.GetResult();
                                                                                                  

                                                                                                  which would actually have David read in the background.

                                                                                                  1. 2

                                                                                                    That said, this post inadvertently reminded me of how few people actually get what C# async and await do. If you look at the sample output in that example, the astute reader will realize that nothing happens in the asynchronous task until .Wait() is called. That’s because Main() is not itself an asynchronous method, and the author never set up a task runner, so this program actually runs entirely in a single thread.

                                                                                                    This is largely inaccurate. ThinkAboutIt doesn’t run when Wait is called, it will immediately begin running on Main’s thread until it hits the first await (the one inside ReadTheManual), at which point control returns to Main, and the rest of the logical thread of control will be on a background thread (not on the initial thread).

                                                                                                    You can verify this yourself by inserting a few prints of Thread.CurrentThread.ManagedThreadId.

                                                                                                    1. 1

                                                                                                      Huh, I apologize; you’re correct. That said, I’m now very confused. When async/await was first introduced, I remember Microsoft screaming about how you needed to be in an event loop for it to work properly, and I remember having to lean on third-party runloops (e.g. Nito.AsyncEx) to get the behavior that’s going on here. (In fact, IIRC, the behavior of await was to return immediately to the parent thread—which, since it was called directly from Main here, would be the OS, which resulted in the program terminating.)

                                                                                                      Did the implementation change, or is there now an implicit run-loop, or maybe I’m conflating this behavior with having Main itself be async (which I think they’re going to allow in the next C# revision anyway)? Any idea what I’m remembering here?

                                                                                                      1. 1

                                                                                                        Maybe the very early CTPs did that? The F# version uses ‘cold’ tasks but C# has been hot since I’ve been using it…

                                                                                                        1. 1

                                                                                                          To my knowledge, await has always ran its target on the current thread until an actually asynchronous operation takes place. This could be something like waiting for a thread to complete, or async IO to finish.

                                                                                                          if you have an async method that skips doing any real async work (maybe you have a cache or something?), you don’t want to have to launch a new thread for that method.

                                                                                                    1. [Comment removed by author]

                                                                                                      1. 9

                                                                                                        The comment about sharing approaches that didn’t work made me want to share the docs for my favorite function in the Haskell world, a variant of unsafePerformIO: accursedUnutterablePerformIO

                                                                                                        -- | This \"function\" has a superficial similarity to 'unsafePerformIO' but
                                                                                                        -- it is in fact a malevolent agent of chaos. It unpicks the seams of reality
                                                                                                        -- (and the 'IO' monad) so that the normal rules no longer apply. It lulls you
                                                                                                        -- into thinking it is reasonable, but when you are not looking it stabs you
                                                                                                        -- in the back and aliases all of your mutable buffers. The carcass of many a
                                                                                                        -- seasoned Haskell programmer lie strewn at its feet.
                                                                                                        --
                                                                                                        -- Witness the trail of destruction:
                                                                                                        --
                                                                                                        -- * <https://github.com/haskell/bytestring/commit/71c4b438c675aa360c79d79acc9a491e7bbc26e7>
                                                                                                        --
                                                                                                        -- * <https://github.com/haskell/bytestring/commit/210c656390ae617d9ee3b8bcff5c88dd17cef8da>
                                                                                                        --
                                                                                                        -- * <https://ghc.haskell.org/trac/ghc/ticket/3486>
                                                                                                        --
                                                                                                        -- * <https://ghc.haskell.org/trac/ghc/ticket/3487>
                                                                                                        --
                                                                                                        -- * <https://ghc.haskell.org/trac/ghc/ticket/7270>
                                                                                                        --
                                                                                                        -- Do not talk about \"safe\"! You do not know what is safe!
                                                                                                        --
                                                                                                        -- Yield not to its blasphemous call! Flee traveller! Flee or you will be
                                                                                                        -- corrupted and devoured!
                                                                                                        --
                                                                                                        {-# INLINE accursedUnutterablePerformIO #-}
                                                                                                        accursedUnutterablePerformIO :: IO a -> a
                                                                                                        accursedUnutterablePerformIO (IO m) = case m realWorld# of (# _, r #) -> r
                                                                                                        
                                                                                                        1. 5

                                                                                                          Notably absent from that comment is any indication of why accursedUnutterablePerformIO is accursed and unutterable, which means if I were performing a detailed documentation audit, I would flag this as needing attention. Maybe it’s obvious to people who can decode Haskell’s sigil soup, but I doubt it.

                                                                                                          1. 4

                                                                                                            For this function it’s really a “if you have to ask, you shouldn’t use it” (it’s also listed as deprecated, another indication that it shouldn’t be used).

                                                                                                            1. 4

                                                                                                              “There are gates you do not open, there are seals you do not breach! The fools who can’t resist meddling are killed by the lesser perils early on, and the survivors all know that there are secrets you do not share with anyone who lacks the discipline to discover them for themselves! Every powerful wizard knows that!”

                                                                                                            2. 1

                                                                                                              I can barely read Haskell but it does explain why: it leads to code that was intended to create multiple distinct mutable buffers to be built to reuse the same mutable buffers instead.

                                                                                                          2. 9

                                                                                                            Once you start writing quality code comments, you can use a tool like Sphinx or Javadoc to include them in your documentation.

                                                                                                            I’ve seen this attempted, but I’ve never seen it work. I would be interested in hearing from people who have had this work on a non-trivial project at your job. So far I’m filing this in the “promising idea that didn’t quite work out” bin.

                                                                                                            In at least the case of library/API documentation, I’ve never seen any other approach work. I’ve also seen this one fail frequently, but it’s at least better than the usual alternative, which is to auto-generate skeleton API docs without useful comments.

                                                                                                            For example, the Java docs, which are auto-generated from comments, are pretty decent, far better than most API/library documentation. CPAN on average is also fairly good, since the Perl community seems to have a fairly strong culture of using POD. Among other things I’ve had the pleasure of using lately, they’ve all been auto-generated too, but by teams that evidently put less emphasis on writing good comments to auto-generate from: Google’s Firebase, Apple’s API docs, etc., all could use a little more time spent on the comments.

                                                                                                            One distinction I sort of like is the Lisp one (adopted by a few other languages) where comments proper and docstrings are both conceptually and syntactically different. In good Lisp style, any function that’s externally callable is supposed to have a docstring at the top of the function, which explains in prose what the function does, and any gotchas/prerequisites/notes/etc. about its usage. This can be both used to auto-generate documentation, and queried at the REPL or whatever IDE you have hooked up. The actual implementation code interior to the function, though, is ideally supposed to be self-documenting, with relatively few comments only where really necessary.

                                                                                                            1. 2

                                                                                                              I think a big part of the problem is that there’s no simple, widespread way to link code to documentation aside from comments. At least in the places I’ve worked, historical discussions, business case documentation, and bug reports are incredibly valuable for understanding and modifying code… if I know they exist and if I can find them. Similarly, most people don’t read the documentation I write. Comments are a terrible way to carry information but they’re also the only way that everybody knows and is supported everywhere.

                                                                                                              It’s a field I keep coming back to and if there’s interesting stuff out there that escaped the “didn’t work out” bin I’d love to research it.

                                                                                                            1. 1

                                                                                                              Today I was thinking about this again, and I found a way to do it in Haskell. The main idea is to represent units of measure as function spaces.

                                                                                                              @stepchowfun did you come up with this ex-nihilo or are there other examples of this you can point to?

                                                                                                              1. 1

                                                                                                                I feel this is closely related to fractional types, where 1/x is interpreted as a promise to receive some x. Then some type 1/x*y represents some computation that requires an x, produces an y. Clearly this post corresponds with that intuition.

                                                                                                                See https://www.cs.indiana.edu/~sabry/papers/fractionals.pdf

                                                                                                              1. 1

                                                                                                                Are there any projects that wrap Win32 so you don’t get any Windows.h pollution? (And provide nice RAII types for everything?)

                                                                                                                1. 1

                                                                                                                  MFC does a lot of what you’re asking, and has been supported by Microsoft since the 90s.

                                                                                                                  1. 1

                                                                                                                    CNoThanks :)