HNHacker News
TopNewBestAskShowJobs

andolanra

1,874 karma · joined July 19, 2010

submissionscomments
andolanra··on New inline assembly syntax available in Rust nightly
This does not make Rust less safe. The capability to use assembly is already there: either by using the existing assembly syntax, by linking to C code, or by linking to assembly routines directly. In order to use this, you need to use the unsafe keyword in order to indicate to Rust, "I am aware that the guarantees that Rust provides to me are not applicable when using this feature." This is true of all the existing ways of interfacing with non-Rust or assembly code, and it does not change with this new syntax.
andolanra··on What the Hell Is a Deno?
One thing you can do is explicitly revoke permissions. This allows you to start the program with more permissions and then give them up as the tasks that required them are done, e.g. allowing environment variable access at the beginning and then revoking it before you start a server: https://deno.land/manual/examples/permissions

It's not as fine-grained as allowing libraries specific permissions, but it gets you part of the way there.

andolanra··on Examples of Incorrect Abstractions in Other Languages
Because Python's design philosophy is that, for a given operation exposed by the language, there should ideally be a single clear way of doing it, and that philosophy is optimized around reading code rather than writing it. You might need to do a few more edits while you're writing the code, sure, but now a reader of your code will always find sum for numbers and join for strings without even having to examine the context of the code in question.

I'm not saying that's necessarily the correct design approach, but it's the one that Python has explicitly established as their guiding principle! A different language will take a different approach: Ruby, for example, likes to build multiple alternative ways of accomplishing the same thing, which leads to multiple aliases for built-in methods or various alternative flags that allow a method to behave in a number of ways. It shouldn't be a surprise that Ruby is fine with the equivalent operation:

    irb(main):001:0> ["a", "b"].sum(identity="")
    => "ab"
But that's because Ruby's design sensibility is different from Python's. You might like one better than another, but they both come from a consistent vision of how to design programming languages.
andolanra··on Examples of Incorrect Abstractions in Other Languages
Some of these are arguably "incorrect abstractions", but others are just design decisions that are being treated as "incorrect" because they don't follow Haskell's design sensibilities. A perfect example is the complaint about sum in Python:

   >>> sum(["a", "b"], start="")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: sum() can't sum strings [use ''.join(seq) instead]
I don't think this is incorrect at all! There's a very good reason to use join instead of sum for adding strings together: join is a lot more efficient than simply adding strings together, because it will precompute the amount of space needed for the resulting string at once and then put all the strings into the buffer at once, whereas using sum in the naïve way suggested would do a different append for each element. These are just different tools for different purposes!

Could you design a sum that automatically looked at its argument type and deferred to join for strings? Sure! And that'd be a valid design decision as well. But the specific choice Python made here is motivated and defensible: it's only "incorrect" if your personal definition of "correct" is "built with the same algebraic sensibility that Haskell's designers had," but at that point, all other languages are "incorrect" because they're not Haskell!

andolanra··on Do notation considered harmful
This is a very old opinion, and one I don't think has aged terribly well. How old? Partway down the page there's a link called "new developments" that cites GHC 6.12, a version that came out in 2009!

Some thing it cites are still reasonably true, but others are complaints that reflect the Haskell of a different era. For example, it says, "Even more unfortunate, the applicative functors were introduced to Haskell's standard libraries only after monads and arrows, thus many types are instances of Monad and Arrow classes, but not as many are instances of Applicative." This has not been true for about half a decade, when Applicative was made a superclass of Monad. The article follows with: "There is no special syntax for applicative functors because it is hardly necessary." This is also no longer true, since the introduction of ApplicativeDo: even if you don't like it (and I confess I'm not a huge fan of ApplicativeDo myself!) it turns out that many people do find that sugar easier to write.

However, I think the key point I'd make is: all the advantages listed here of using monad without do-notation are real in some cases, but there's nothing stopping you from not using do-notation sporadically! Occasionally a snippet that uses do-notation can be made much clearer by using >>= or some other monadic function, and occasionally a snippet that uses >>= can be made much clearer by using do-notation, and I don't think there's anything inherently wrong with Haskell keeping both. To make a Python analogy: sometimes you can express a given construct more clearly with a loop than with a list comprehension, but I don't think that list comprehensions should be considered harmful: just use those tools when you need them, and don't use them when you don't need them!

andolanra··on ActivityPub Could Be the Future
You can't import your posts, but they will still exist at your old account: if I move from foo@social.example to bar@social.example, then all the posts I made as @foo are still visible on @foo's timeline, rather than getting moved to @bar. (You would have two copies of all your posts otherwise, since people can still fav/boost/interact with the post made when you were still @foo.) Moving accounts will redirect your followers to the new account, and you can import all your old follows/likes/&c to the new account.
andolanra··on Creating Your Own Git Server
I now use a personal Gogs instance, but I also want to give a shout-out to Klaus: it's something like Gitweb with a more Github-like UI. Klaus (like Github) shows you first and foremost the state of the tree with nicely rendered or syntax-highlighted file contents instead of (like Gitweb) focusing on the commit history first. It also has smart Git HTTP support out of the box without having to muck around in the web server config as described in this post.

The project is here https://github.com/jonashaag/klaus and you can see a preview of it here: http://klausdemo.lophus.org/

andolanra··on Object-oriented Haskell surprisingly appealing
I'm not necessarily going to go to bat for the specific encoding of OOP Haskell in the linked post, but in general, there are problems where objects are a good fit. William Cook (in the paper On Understanding Data Abstraction, Revisited) claims that the key feature of objects is that they are defined in terms of an external interface whose internals are hidden: this captures, for example, SmallTalk-style message sends and duck typing as well as the information-hiding found in languages like C++ or Java. One way of encoding this idea into Haskell is with records of functions: to give a tiny example, a set type can be represented as

    data Set a = Set { contains :: a -> Bool }
which leaves the actual structure of the set undefined. This means you can use a variety of methods 'under the surface' to compute set membership and the external interface can be identical and compatible:

    emptySet = Set { contains = \_ -> False }
    singletonSet x = Set { contains = \y -> x == y }
    union l r = Set { contains = \y -> contains l y && contains r y }
    fromList lst = Set { contains = \y -> y `elem` lst }
And while this example is a bit trivial, this pattern is invaluable for writing things that OOP can traditionally excel at, like writing GUIs or certain kinds of video games where you want to bundle heterogeneous behavior into a single package:

    data GameObject = GameObject
      { render :: Pos -> GraphicsContext -> IO ()
      , currentPosition :: Pos
      , setPosition :: Pos -> GameObject
      }
Of course, this is only one (significantly simpler!) encoding of OOP, and it might not be one that you think captures the key ideas of objects, but to the degree that it is it demonstrates that ideas which are common in OOP (like information-hiding behind an interface) can still be useful in Haskell when judiciously applied in the right context.

The paper I cited can be read here, which has more details: https://www.cs.utexas.edu/~wcook/Drafts/2009/essay.pdf

andolanra··on First, Consider No Harmful
"Please don't fall into the trap of believing that I am terribly dogmatical about [the goto statement]. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a single trick, by a simple form of coding discipline!"

—E. W. Dijkstra

andolanra··on Every Google result now looks like an ad
My Firefox install has seven search engines by default in the "one-click search engine" options. By contrast, DuckDuckGo has 13000 bang shortcuts. Sure, you don't need all of them, but I use dozens: some of them almost daily (like !w for Wikipedia and !wt for Wiktionary), some sporadically (like !tw for Twitter or !a for Amazon) and some of them only rarely (e.g. I picked up a Pokémon game recently and consequently have used !bulba to quickly search Bulbapedia, a Pokémon fan wiki.) Of course I can add all those to Firefox, but DuckDuckGo already has them set up, and has many more that I can use without thinking about them. I think that still counts as a killer feature.
andolanra··on Courting Haskell
"[Haskell is], indeed, heavily founded on mathematical theories (category theory and lambda calculus)."

This is a pretty common reason given for why Haskell is "math-ey", but I'd argue it's simultaneously a bit misleading and also a bit boring. The part that's misleading is "Haskell is based on category theory": while a major feature of programming in Haskell (the monad abstraction) was inspired by category theory, the truth is that moment-to-moment programming in Haskell doesn't have much to do with category theory unless you want it to. Even monads require zero knowledge of category theory in order to understand or use! Some people find usefulness in category-theoretic abstractions, but many others—myself included!—don't at all: I've actually improved the performance of Haskell code in the past by cutting out category-theoretic abstractions in favor of simpler code, and by now I steer clear of most Haskell code that wears category theory on its sleeve.

The part that's boring is that "Haskell is based on lambda calculus". The lambda calculus is a mathematical description of a simplified model of computation with functions and binding… and that's basically it. Almost every modern programming language uses variable binding as a basic feature, and in that sense, is "based on lambda calculus". Plenty of other languages, for example, are inspired deeply by Lisps (like JavaScript or Ruby) which in turn were directly inspired by the lambda calculus, but I don't think that lineage makes them particularly math-like. Instead, being "based on lambda calculus" usually just means that they have variables and closures—something true of almost every popular language used today!

Now, Haskell-as-practiced can have a fair bit of math-inspired abstraction in it, which can be daunting to someone new to Haskell. Some of that abstraction is useful (e.g. monads), some of it isn't, but the fact that it exists is more about vocal Haskell programmers and bloggers, and much less about Haskell-the-language being "heavily founded on mathematical theories". And you'll also find plenty of programmers and bloggers arguing against the more math-heavy parts of the ecosystem: just in the past month I've seen plenty of Haskell programmers passing around links like https://www.simplehaskell.org/ which advocates for exactly this less-mathy approach to Haskell.

andolanra··on How to make roguelike games in Rust
I said this in a previous comment on a different story, so I'm going to copy-paste the content of that comment with some minor edits:

The bfnightly tutorial is a very good tutorial, but I do want to warn that it's not always terribly idiomatic Rust: Herbert was using the writing of the tutorial as a way of practicing Rust, so there are places where the explanations aren't quite correct and other places where the code isn't what an experienced Rust programmer would write. An example that jumped out at me (this from Chapter 1.7) was the use of

    &s.to_string()
which is an expression that could be shortened to just s. (Specifically, s has type &str, calling to_string() on it converts it to a String, and borrowing it allows it to be coerced back to a &str but now with unnecessary copying.) Elsewhere (this from Chapter 1.2) it has explanations like

> Copy and Clone allow this [type] to be used as a "value" type (that is, it just passes around the value instead of pointers)

which is also not a correct explanation, or at least is a misleading one: things can still be passed by value even without Copy and Clone and without any pointers at all. (Arguably, he meant, "You can share the value without using borrows," but that's not what the explanation said.)

That said, all this is nitpicky (and I suspect that Herbert would be receptive to feedback along these lines—I just haven't had a chance yet to provide it!) because the tutorial's explanation of roguelike-writing is still very good, and in fact I've been following it as a rough guide for writing a tutorial on top of a different Rust library myself! But I do want to warn about using it as a model for learning Rust specifically.

andolanra··on Cultural Context of “Death to America” Chants
The text does indeed say marg bar America, the phrase being discussed in the article. The bombs aren't supposed to indicate "let's drop them on America", but rather are intended to represent the bombs dropped by Americans: the intent of the mural is to criticize America's military interventions.
andolanra··on Roguelike Tutorial: Up-to-Date and Literate
The bfnightly tutorial is a very good tutorial, but I do want to warn that it's not always terribly idiomatic Rust: Herbert was using the writing of the tutorial as a way of practicing Rust, so there are places where the explanations aren't quite correct and other places where the code isn't what an experienced Rust programmer would write. An example that jumped out at me (this from Chapter 8) was the use of

    &s.to_string()
which is an expression that could be shortened to just s. (Specifically, s has type &str, calling to_string() on it converts it to a String, and borrowing it allows it to be coerced back to a &str but now with unnecessary copying.) Elsewhere (this from Chapter 3) it has explanations like

> Copy and Clone allow this [type] to be used as a "value" type (that is, it just passes around the value instead of pointers)

which is also not a correct explanation, or at least is a misleading one: things can still be passed by value even without Copy and Clone and without any pointers at all. (Arguably, he meant, "You can share the value without using borrows," but that's not what the explanation said.)

That said, all this is nitpicky (and I suspect that Herbert would be receptive to feedback along these lines—I just haven't had a chance yet to provide it!) because the tutorial's explanation of roguelike-writing is still very good, and in fact I've been following it as a guide for writing a tutorial on top of a different Rust library myself! But I do want to warn about using it as a model for learning Rust specifically.

andolanra··on Today is Esperanto Book Day – why I learned it (2018)
I own a handful of Esperanto books, most of them translations that range in quality (e.g. a surprisingly good translation of The Hobbit that also translates both the poetry and the runes on the map, but also an abysmally bad translation of Alice in Wonderland that abandoned any attempt to preserve the wordplay) but the native-Esperanto books I do have are often endearingly clunky in a way that marks them as obviously self-published. For example, one of them regularly misprints its own title: the cover and the spine don't even agree on the name of the book!
andolanra··on Are We Wayland Yet?
Servo is not a browser, it is a browser engine. To quote the Mozilla Research page about it: "Like WebKit, the Servo project is not a complete browser. It is an experimental project that delivers components that can load, run, and display web sites and applications." Given that, it doesn't make sense to talk about users "…switching from Firefox to Servo."

c.f. https://research.mozilla.org/servo-engines/

andolanra··on Typestates in Rust (2018)
If you were to open a file that didn't exist and then not use the result at all, then you get a warning (which can be made an error, if you want):

    File::open("foo");
    // warning: unused `Result` that must be used
But that's also not necessarily an invalid program, it's just not a very useful one. Failing to open a file doesn't interrupt the program in any way, so File::open here will return either a file handle or an error, neither of which get used. However, if you open a file, don't check for an error, but still try to use the file, then your program won't compile:

    File::open("foo").read(&mut buffer);
    // error: method `read` not found in `Result<File, Error>`
You need to be explicit about what to do in case the file didn't open, and Rust won't let you use your file handle unless you explicitly indicate what to do in that error case!
andolanra··on Typestates in Rust (2018)
You should probably have read the rest of the blog post, because in Rust it is a compiler error to not test the result, and the post specifically calls this out. Specifically, look for this section:

    let mut my_file = MyFile::open(path)?;
    // Note the `?` above. It's a simple operator that asks
    // the compiler to check whether the operation succeeded.
    // The *only* way to obtain a `MyFile` object is to
    // have a successful `MyFile::open`.

    // At this line, `my_file` is a `MyFile`, which means
    // that we may use it.
andolanra··on Python is not context free (2012)
There are some papers (which postdate this blog post) that add simple extensions to context-free grammars that are capable of expressing the grammars of indentation-based languages in a principled way: Principled Parsing for Indentation-Sensitive Languages (Michael Adams, 2013)[1] and Indentation-Sensitve Parsing for Parsec (Michael Adams, 2014)[2], which add support for indentation-based parsing to bottom-up parsers (specifically GLR and LR(k)) and top-down parsers (parser combinator-like systems and PEGs) respectively.

These techniques do not "make Python context-free", so the blog's content stands. However, they do help address the final point of this blog post: they provide principled and limited tools which can express this kind of non-context-free grammar in a declarative way. Tools like these allow us to use simple lexers (i.e. without state hacks like Python's) while expressing indentation-based grammars using easy-to-understand-and-analyze BNF-like formalisms.

[1]: https://michaeldadams.org/papers/layout_parsing/ [2]: https://michaeldadams.org/papers/layout_parsing_2/

andolanra··on Why I Prefer Functional Programming
> …you have to remember Haskell's list comprehension syntax…

I get the point you're trying to make, but this feels like a huge stretch. Haskell's list comprehension syntax is straightforward and in many cases a terse and useful way of implementing complex functionality. You'll find multiple uses of it in the base libraries; notably, catMaybes is implemented in terms of list comprehension:

    catMaybes :: [Maybe a] -> [a]
    catMaybes ls = [x | Just x <- ls]
I'm all for reducing cognitive load, but Haskell has such a sparse syntax and this specific feature is so useful (and honestly, so consistent with the rest of Haskell's syntax) that arguing it's some kind of unnecessary cognitive burden feels ridiculous to me.
andolanra··on Why I Prefer Functional Programming
I think this is a question that's orthogonal to OOP-versus-functional-programming, but is rather about programming style. Haskell programmers like being terse, but there's also nothing stopping you from writing Haskell like this:

    alignCenter :: [String] -> [String]
    alignCenter lines =
      let maxLineLength = maximum (map length lines) in
      [ leftPadding ++ line
      | line <- lines
      , let leftoverSpace = maxLineLength - length line
            leftPadding = replicate (leftoverSpace `div` 2) ' '
      ]
You could have verbose and easy-to-read functional code—you don't see this often in Haskell by convention, but you might in OCaml or in Scheme—and you could also have terse OOP code filled with single-letter variable names (less so in Objective C by virtue of the method call syntax, but I've definitely seen code like this in Java and JavaScript.)
andolanra··on Why is Rust slightly slower than C?
Others have pointed out that Rust's type-checking is all done at compile time. Rust also does not have exception handling.

To be more specific: errors in Rust are either propagated by function return values (usually in the form of `Result` or `Option` types) which do not require any kind of runtime support, or by panicking, which unwinds the stack for clearing-up of resources and then halts the running thread. (Panics cannot be caught, although they can [EDIT: sometimes but not always; see burntsushi's comment below] be observed by a parent thread if they have occurred in a child thread.) Rust's "runtime", therefore is negligible, much like C's: it consists mainly of functions which can be called for things like stack unwinding or backtrace support.

andolanra··on Thoughts on Rust bloat
One thing I like about Rust a lot is that it lets you choose which hit you want to take when it comes to polymorphism. You can manually (and without much difficulty!) prefer dynamic dispatch if it's important to you to keep your binary size small, but you can also choose static dispatch and allow some replicated copies of your parametric code if that's what you want to optimize for.

It's also worth noting that if you are using polymorphic functions that are only ever called in your code with a single known type parameter, then your program should be just as efficient as if you wrote a monomorphic version with that fixed type in both runtime _and_ binary size, which means the only disadvantage to the polymorphism there is compile time.

andolanra··on Interslavic Language
The thing Interslavic is doing has also been tried before, but the illustrative example isn't Esperanto (which is an ad-hoc mixture of words taken arbitrarily from existing languages and put into a synthetic grammar) but rather Interlingua, which is a kind of streamlined Romance language. It too is more-or-less comprehensible by people who speak any Romance language, and was intended to be used in much the same way. In fact, to quote the creator of Interlingua writing in Interlingua:

    Le sol facto que importa (ab le puncto de vista de interlingua mesme) es que
    interlingua, gratias a su ambition de reflecter le homogeneitate cultural e ergo
    linguistic del occidente, es capace de render servicios tangibile a iste precise
    momento del historia del mundo. Il es per su contributiones actual e non per le
    promissas de su adherentes que interlingua vole esser judicate.
(Edit: to translate for non-Romance speakers, this means, "The only important fact (from the point of view of Interlingua itself) is that Interlingua, thanks to its goal of reflecting the cultural—and therefore linguistic—homogeneity of the West, is capable of providing tangible services at this precise moment in the history of the world. It is for its actual contributions, and not the promises of its adherents, that Interlingua wishes to be judged.")

It too has met with only limited success: there were some short-lived scientific publications in Interlingua, for example, for the intended audience of all Romance-speaking scientists. Even so, it never attracted much of an audience. That's not to say such a thing could never happen: merely that it's not unreasonable to be pessimistic here.

andolanra··on Ithkuil: A Philosophical Design for a Hypothetical Language
A friend of mine one jokingly but reasonably accurately described Ithkuil as, "Build a language with as much derivational morphology as humanly possible, then add some more, and you've got Ithkuil." It's a fascinating experiment and a lot of fun to read about.

As a fascinating side-note: the language was also at one point adopted by some kind of fringe pseudo-religious movement called "Psychonetics" as a "…technology of human consciousness." There's a fascinating article about Ithkuil, its creator, and its adoption by that movement here: https://www.newyorker.com/magazine/2012/12/24/utopian-for-be... (and the previous HN discussion of it here https://news.ycombinator.com/item?id=8180493 )

andolanra··on What's Coming in Python 3.8
They address this briefly in the PEP[0], and it's largely because they want to be very clear about when it's happening. The distance between

    if x = y:
and

    if x == y:
is very small, and easy to ignore, so insisting on

    if x := y:
makes it very clear that what's happening won't be mistaken for comparison at a quick glance.

[0]: https://www.python.org/dev/peps/pep-0572/#why-not-just-turn-...

andolanra··on Infinite work is less work
For what it's worth, I've worked as a professional Haskell programmer at multiple companies and have been writing Haskell for more than a decade, and I still have trouble reading code written in that style. I usually end up decomposing it at a REPL so I can look at the types of intermediate parts of the program.

(In practice, most—although not all, admittedly—of the Haskell codebases I've worked with tended to be a lot less dense than the occasional combinator-heavy sample code you see in blog posts and comments.)

andolanra··on Open-sourcing Sorbet: a fast, powerful type checker for Ruby
Sorbet has two components, broadly speaking: the typechecker, which is a standalone application, and the runtime, which is a Ruby gem. The typechecker can parse Ruby code and find the sig blocks in the code to extract type information from them, and it then can use this type information to perform type-checking without ever loading a Ruby interpreter, to say nothing of the actual Ruby code in question. This is intended to happen in a CI pass or a pre-processing step, but it is entirely offline.

On the other hand, when you run your code, the sig blocks may or may not be used. There's a lot of machinery in https://github.com/sorbet/sorbet/blob/master/gems/sorbet-run... that handles understanding what a sig means and installing a wrapped version of a method that does type-checking on entry and exit. The intention is that the standalone sorbet executable should be used during development, but it can't catch every error, so the runtime system will double-check types at runtime, which is especially helpful when some parts of your code are typed and other parts are untyped, and control flow passes back and forth between those sections: the runtime will ensure that you don't accidentally pass an object with an unintended runtime type into code with static type expectations.

andolanra··on Alchemist – A non-deterministic programming language based on chemical reactions
After poking around the site, I can't see any specific reference—which makes me suspect it was an independent discovery of a cool idea—but this is very similar to a deliberately-constrained version of the Join Calculus https://www.microsoft.com/en-us/research/wp-content/uploads/... which uses a similar chemical-ish model of computation. There's an implementation of the Join Calculus in an OCaml-like language called (of course) JoCaml. Where Alchemist would write

    2H + O -> H2O
JoCaml would write

    def h() & h() & o() = h2o();;
One of the big differences is that JoCaml allows you to embed payloads inside your atoms, e.g.

    def a(x) & b(b) = c(if b then x else 0);;
will consume an a with an integer payload and a b with a boolean payload, and produce a c with an integer payload. It also lets you include computations as reactions happen, e.g.

    def a(x) = ( Printf.printf "Reacting!\n"; b(x + 1) );;
which lets you use this "chemical soup" model as the basis for concurrent programming.

There is a very detailed tutorial (which appears to have sadly half-broken formatting) which explains JoCaml in more detail here: https://sites.google.com/site/winitzki/tutorial-on-join-calc...

andolanra··on Another dubious claim that someone has “decoded” the Voynich manuscript
The Voynich manuscript has been pretty reliably dated to the early 15th century by both carbon dating and by comparison of the materials, techniques, and style used in the manuscript—tens of thousands of years, at least, after a proto-human language would have been spoken. Language change happens pervasively, even in isolated speaker communities, so even if there had been some isolated enclave that cut off all contact when proto-human was still being spoken, it's vanishingly unlikely that they would have continued speaking perfectly preserved proto-human tens of thousands of years later: their enclave's language would almost certainly have gradually changed into a modern descendant, and the lack of contact with other languages would have made it appear to be a language isolate (i.e. a language with no observably related languages, such as Basque, Ainu, or Haida). That's not unlikely: in fact, if the proto-world hypothesis is correct, then that's how all language isolates did in fact form! But we have no evidence at all to suggest that a speaker community either would or could speak an unmodified form of a language for tens of thousands of years.

The Sentinelese language hasn't been studied at all—it's hypothesized that it's related to the nearby Ongan languages (spoken in the same island chain as Sentinel Island) but when Onge speakers have attempted to communicate with the Sentinelese, they haven't been able to understand what's being said. (Which doesn't disprove the hypothesis: English and German spring from a common ancestor, but if an English speaker heard German for the first time, they too would have no idea what was being said!) But as I said above: just because a group is isolated, that doesn't mean that their language isn't changing just as much as others—it might just be changing in different directions!

← PreviousPage 2 of 10Next →