HNHacker News
TopNewBestAskShowJobs

phs2501

1,017 karma · joined July 2, 2014

submissionscomments
phs2501··on Is Clojure dying, and what has Ruby got to do with it?
Well yes, in theory, but it's generally considered in somewhat bad taste to replace fundamental basics of the language. At that point you're not really writing Clojure anymore, it's hard to read other people's code, it's hard for other people to read your code, etc. See the somewhat controversial Allegro CL IF* macro for an example of this (I think that's what it was called; turns out that's really hard to search for).

It's a little different if you're either creating a domain-specific language (at that point you've already made the decision to have a new language, so go nuts) or if you're adding new control structures that you can't have in the base language without excessive boilerplate (see for example the somewhat common AIF macro, which binds its conditional result to a variable). But I'd consider making a new MY-LET macro because I don't like what the built-in LET looked like to be a bit gauche.

Regardless, I just decided to stick with the Lisp dialect that already worked the way I wanted.

phs2501··on Is Clojure dying, and what has Ruby got to do with it?
Oddly, as someone who was reasonably into Common Lisp, what really viscerally turned me off to Clojure was the use of square brackets.

This sounds petty but I actually have some rationalization for it. Once you learn to read Lisp (mostly looking at the indentation and ignoring the parens) it's really nice that there's only one kind of delimiter in the language. It allows a lot of easy structure editing with a decent editor. I had emacs set up to use the unshifted square bracket keys to create and exit balanced parenthesis pairs. Once you got used to this, this was really pleasant to write and mutate code.

As soon as I saw Clojure, I knew that my setup would unavoidably get twice as complicated because they added another delimiter that's used in random places in the code. (Why are function arguments a vector instead of a list? Just because the designer thinks it looks better? Why are let bindings a vector? Why are ordinary function calls and function bodies a list? It just feels arbitrary.) That in and of itself really turned me off. (The fact that it's the JVM really didn't help either, but that was actually secondary to the above.)

Other issues for me were the lack of two layers in the LET statement (i.e. it's (let [a 1 b 2] ...) rather than (let ((a 1) (b 2)) ...). To some that probably "looks better" but it kills the ability to use emacs's transpose-sexps to swap the order of your let bindings around.

All in all the syntax just didn't seem well thought out to me. OTOH to non-lispers it probably looked better, so maybe that was the goal.

phs2501··on Consider a Dell Optiplex FX160 Instead of a Raspberry Pi
They make/sell "industrial" fanless Intel boxes. I got one of these[1] for a router, and it's worked well so far. No moving parts with an SSD.

[1] https://smile.amazon.com/gp/product/B01KX9OU58

phs2501··on Why Japan’s Rail Workers Point at Things
I dunno, it seems a dramatically more reasonable response to preventing accidents than the disaster that is PTC[1][2], which has cost billions and will probably never work properly.

[1] https://www.aar.org/policy/positive-train-control

[2] https://en.wikipedia.org/wiki/Positive_train_control

phs2501··on Node v7.5.0 Released
A Promise/async/await implementation of 'async.auto' is trivial, as you can use the behavior of promises themselves to avoid solving the graph:

  async function Promise_auto(tasks) {
      const keys = Object.keys(tasks);
      const results = { };
      const taskPromises = { };
      function runTask(key) {
          if (!taskPromises[key]) {
              taskPromises[key] = (async () => {
                  let fn = tasks[key];
                  if (fn instanceof Array) {
                      const deps = fn.slice(0, -1);
                      fn = fn.slice(-1)[0];
                      await Promise.all(deps.map(runTask));
                  }
                  results[key] = await fn(results);
              })();
          }
          return taskPromises[key];
      }
      await Promise.all(keys.map(runTask));
      return results;
  }
Usage example (terms intentionally out of order):

  (async () => {
      const start = new Date();
      const results = await Promise_auto({
          write_file: ['get_data', 'make_folder', async (results) => {
              console.log('in write_file', results);
              await Promise.delay(1000);
              return 'filename';
          }],
          email_link: ['write_file', async (results) => {
              console.log('in email_link', results);
              await Promise.delay(1000);
              return {file: results.write_file, email: 'user@example.com'};
          }],
          get_data: async () => {
              console.log('in get_data');
              await Promise.delay(1000);
              return [ 'data', 'converted to array' ];
          },
          make_folder: async () => {
              console.log('in make_folder');
              await Promise.delay(900);
              return 'folder';
          },
      });
      console.log('results = ', results);
      console.log(`It took ${(new Date() - start) / 1000} seconds`);
  })();
phs2501··on Why hasn't Haskell taken over the world? And the curious case of Go
I kind of like Haskell, but the learning curve is brutal and the documentation is often intensely non-helpful if you don't want to learn a whole lot of mathematical terms.

i.e. when browsing and trying to understand parts of the XMonad code, I came across the "Endo" type. I Hoogle'd it, and the only documentation for it is "newtype Endo a: The monoid of endomorphisms under composition." Thats.... not helpful for me.

phs2501··on C99/C11 dynamic array that mimics C++'s std::vector
I think it's cleaner to allocate the header information before the actual pointer (or, conversely, to return the pointer sizeof(array_header) into the allocation). Then it's trivial to pass around.

  struct vec_header_t {
      size_t length;
      size_t capacity;
  };

  static inline struct vec_header_t *vec_to_header(void *vec)
  {
      return ((struct vec_header_t *)vec) - 1;
  }

  #define _vec_length(vec) (vec_to_header(vec)->length)

  static inline void vec_free(void *vec)
  {
      if (vec)
          free(vec_to_header(vec));
  }

  #define vec_foreach(vec, iter) \
      for ((iter) = (vec); (iter) < ((vec) + vec_length(vec)); ++(iter))
It's slightly more cognitive overhead when, for example, debugging, but the vast improvement in usability (no special macros for normal/static declaration, trivial passing to functions, etc) is worth it IMHO.
phs2501··on Use "App folder" access type instead of "Full Dropbox"
If 1password has been injected with malicious code, whoever has done so will have all your encrypted credentials the next time you unlock your vault, including presumably your full Dropbox creds.

(Caveat: I use Keepass2Android, which ironically DOES support limiting access to the Apps folder in Dropbox.)

phs2501··on Why isn't memset() async-signal-safe?
How would you handle being able to trap and fix bad memory access (i.e. hooking SIGSEGV/SIGBUS) with this scheme? Lots of programs use this for various reasons; off the top of my head at least a few generational GCs implement their write barrier by setting pages read-only and noticing the trap and resetting RW on SEGV...

(Yes, I believe something like card marking is more performant than this in several ways on modern CPUs, but then you need to insert card marking instructions into your code generator... trapping SEGV is easy and only happens in the GC.)

Also there's all the debugging and process inspection stuff that works via signals, all of which depends on being able to really interrupt code rather than just stuff a message in a queue.

phs2501··on Debian putting everything on the /usr
initramfs isn't (and wasn't) at all about keeping your boot files below the 2gb mark on a disk. It's about actually mounting your root filesystem, either on a fully-modularized kernel where all of your (hundreds of) block drivers are in modules, or where root is coming over a network block device, or a USB disk, or a SAN, or a complex RAID, or an encrypted disk where you need to enter a password or use a hardware token or whatever.

Basically, rather than encoding tons and tons of crazy special-purpose logic into the kernel to mount every insane iteration of root filesystem, you just get the bootloader to stick a CPIO image in RAM for the kernel to get at and then use the normal userland tools to do what you want.

IMHO, initramfs is the opposite of an ugly hack.

phs2501··on First 64-bit Orange Pi slips in under $20
It almost certainly saves space as the odd placement would be to optimize signal routing. You need a fair amount of space around a BGA to fan out all the signals, and at $20 they certainly have tried to do this with the minimum number of PCB layers possible which makes the routing problem much harder.

That being said it does look really strange.

phs2501··on Angular 1.x Banned from Firefox Addons
So someone found some sort of vulnerability in Angular 1, told Mozilla about it, but told them not to tell the Angular team?

What is going on here?

phs2501··on Ebay posts every character a user types into the password box
If you hash, with or without salt, on the client for changing the password, you'll also need to hash identically when checking it (i.e. for login). In effect, the hash becomes the password; even if the plaintext is never leaked the first-level hash is just as good for access.
phs2501··on PostgreSQL Scalability: Towards Millions TPS
Can't speak for the original poster, but at this point Postgres is a very capable and performant document-store database as well with the addition of the JSONB type and associated indexing and modification (in 9.5) functions. It's also more mature, has generally better durability than MongoDB, and it is easy to fall back/extend into relational queries on your document-store data if need be.

IMHO, unless you have a situation such that Mongo's horizontal scaling is actually required, it's better to use Postgres even if you're doing document-store stuff.

phs2501··on Curl vs. Wget
|& is useful in these cases, as it redirects both stdout and stderr to the piped process's stdin. It's a cshism, but it works in both zsh and modern bash. Much nicer than typing cmd 2>&1 | cmd.

For context try grep -2, where 2 is the desired lines of context.

  $ wget --help |& grep -2 base   
    -i,  --input-file=FILE           download URLs found in local or external FILE
    -F,  --force-html                treat input file as HTML
    -B,  --base=URL                  resolves HTML input-file links (-i -F)
                                       relative to URL
         --config=FILE               specify config file to use
  --
                                       existing files (overwriting them)
    -c,  --continue                  resume getting a partially-downloaded file
         --start-pos=OFFSET          start downloading from zero-based position OFFSET
         --progress=TYPE             select progress gauge type
         --show-progress             display the progress bar in any verbosity mode
(Not that I'm arguing that this is an excuse for wget's [and GNU projects' in general] man pages sucking, but it's a useful workaround.)
phs2501··on ARM immediate value encoding
SPARC. The CALL instruction takes a 30-bit immediate. http://www.cs.rochester.edu/~scott/456/local_only/sparcv7.pd... Also the Mill talks seem to indicate that their (variable-length) instruction encoding is machine-generated to be as efficient as possible.
phs2501··on Google’s look, evolved
From the <link rel="shortcut icon"> presumably.
phs2501··on Ten years later, the story of Suck.com (2005)
Since it died suck.com has been my canonical "poke the public-access WiFi gateway so it'll send me to its clickthrough agreement page" URL, since at least at one point that usually worked via DNS poisoning and I wanted to make sure it didn't poison a domain I actually wanted to visit for real. So I wound up seeing the page relatively often even recently. I was very sad when I saw it had turned into a parking spot sometime earlier this year. :/
phs2501··on Windows 10 Review
But even with cmake there's no standard place for third-party libraries to be installed on Windows. There's no /usr/lib and /usr/include analogue. So if I'm writing software that uses a third-party library I either have to bundle it into my source tree or hope there's an installer for it, burn the default installation location into the build system, and hope the user compiling my code has not overridden the default location. (I know some installers like Qt will make a system environment variable to say where their libraries are, but that's not particularly nice if everything did that.)

In my case I needed libapr, and as far as I can tell there is no installer for it, so I wound up just saying "drop a compiled copy of libapr into this location in the source tree" into the build instructions since I did not want to redistribute it with my source.

So yes, I dearly wish for something like a working pkgconfig on Windows.

phs2501··on Show HN: Git-remote-dropbox – use Dropbox as a true Git server
Also since packs are not used (the design document says it stores all objects as loose objects) there is no delta compression. This won't be usable for large repos with lots of history.
phs2501··on Debugging Lisp Part 4: Restarts
Fair point.

What I was attempting to convey is that the reason I think the CL condition system works so well is that at its core it doesn't attempt to manage control flow directly; i.e. neither HANDLER-BIND nor RESTART-BIND unwind the stack on their own, and even ERROR only gets its behavior by falling back to INVOKE-DEBUGGER if unhandled. Because CL has good tools for doing non-local control flow that aren't bound up in its condition/exception system it winds up strictly more expressive (IMHO) than languages that conflate these.

What I've noticed from languages that have traditional try/catch exception handling (especially when that's the only non-local control flow available) is that the mechanism of error handling is seen as DEEP MAGIC by their users. If you separate it out into non-local control flow and a protocol for communicating exception handlers as CL does, the "magical" properties go away since it can be seen how the condition system could be implemented.

Or at least that's how it worked for me.

phs2501··on Debugging Lisp Part 4: Restarts
Restarts are nice.

The thing is, though, is that restarts are relatively easy to add to any language that has well-supported structures for non-local control flow; they're just a protocol, after all. That's really what Common Lisp has which most other languages lack. Common Lisp (very intelligently, IMHO) did not conflate error handling with non-local control flow; they are different concepts and deserve to be treated independently.

Specifically, the lexcal scoping of BLOCK and TAGBODY labels and the fact that RETURN-FROM and GO will unwind the stack if necessary to get to the target label make creating something like HANDLER-BIND/RESTART-BIND relatively easy. (There is also of course THROW and CATCH [which don't really have anything to do with error handling in CL, they're just structures for dynamically-scoped stack unwinding], but I've generally found the need to create explicit labels for those less elegant than using lexical scoping. You can always use a closure if you need to store the lexicaly-bound state to be used somewhere else, and the reverse is not as easy...)

You obviously also need UNWIND-PROTECT to keep things safe when the stack unwinds, but most languages successfully manage to get that with something like try-finally (or RAII in C++'s case, even if I find it clumsy sometimes).

phs2501··on SpaceX CRS-7 Failure Investigation Teleconference Thread
The F9's fuel is just rocket-grade kerosene, nothing particularly dangerous or exotic. The thrusters on Dragon on the other hand use monopropellent which is very toxic, but Dragon survived and impacted the sea.
phs2501··on Error Handling in Nodejs
No built-in help with native promises, but with something like Bluebird you'd prefer:

   something().then(() => {  
      return Promise.delay(1000).then(() => {  
        whoops_not_a_function();  
      });  
   }).catch(() => {  
      // whoops_not_a_function is not defined  
   });
...or write Promise_delay yourself on top of native promises (but really just use Bluebird).
phs2501··on Error Handling in Nodejs
IMHO, very doubtful; Bluebird has a lot of (very useful) features that would be pretty burdensome to put into a specification.

On the other hand, there's really no reason not to use Bluebird. It's easy to ensure that a promise you receive is a Bluebird promise:

    import Bluebird from 'bluebird';
    const bluebirdPromise = Bluebird.resolve(promise);
If `promise` is a Bluebird promise that will be a no-op, and if it's a foreign promise or thennable you'll get a new Bluebird promise that will resolve/reject when the foreign one does. (This is standard behavior; see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...)

Since Bluebird promises are thennables, the reverse will be true if they're consumed by a different promise library.

Given Bluebird's speed, superior debuggability, and features, there's really no reason not to use it a this point.

phs2501··on Firefox makes click-to-activate Flash the default
They are an American company and America typically counts Sunday as the start of the week.
phs2501··on Why is my x64 process getting heap address above 4GB on Windows 8?
This bit me in a library of mine. Some third-party code I used was using long as memory offsets which failed on Windows 8. Unfortunately I was developing on Windows 7, so it didn't get noticed until the customer stumbled over it. :( (Fortunately said third-party code had an update that switched to ptrdiff_t in all the right places, so it was an easy fix.)

I'm a Unix person, so the notion of long being less than word-length seems pretty silly to me, but I understand why they did it, sort of.

phs2501··on Show HN: A simple password generator
Why is it telling me to press Command+C to copy the password on my Linux machine?

Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0

And yes, anybody who would use a password that was generated on a foreign server is very silly indeed. The password should be generated locally in the browser.

Other than those things the visual design is nice at least.

phs2501··on Why Wolfram Mathematica did not use Lisp (2002)
The syntax looks a lot like M-expressions, but the evaluator is about as far from a normal Lisp as you can get.

Mathematica uses a fixed-point evaluator; that is, expressions continue evaluating until they reach some stable state. Every common Lisp I've seen will only evaluate once unless you explicitly code for it.

While this evaluation behavior is very useful for mathematics, I find that it makes Mathematica a poor general-purpose programming language. It's pretty easy to wind up with confusing evaluation behavior, and controlling evaluation in Mathematica is a very complicated topic. There are myriad language constructs used to do so: Unevaluated, Hold, HoldForm, HoldAllComplete, other Hold*; see http://stackoverflow.com/questions/1616592/mathematica-uneva... and http://library.wolfram.com/infocenter/Conferences/377/ for more information.

phs2501··on FreeBSD Support for Leap Seconds
Smearing is not suitable for all applications, however. For example, anything requiring sub-microsecond precision (i.e. data acquisition) would require at least a 12-day smear to not fail its timing requirements.

Such precison is well outside the realm of NTP but is not really a problem for PTP. I've worked on Linux systems that have done this; ~100ns synchronization was not uncommmon.

(PTP, incidentally, got it right and specifies that its timescale is based on TAI, so it is continuous.)

← PreviousPage 5 of 7Next →