HNHacker News
TopNewBestAskShowJobs

brandonbloom

1,965 karma · joined May 16, 2013

brandon@deref.io

http://www.brandonbloom.name

https://github.com/brandonbloom

https://twitter.com/BrandonBloom

http://www.linkedin.com/in/brandondbloom/

Old account: https://news.ycombinator.com/threads?id=snprbob86

submissionscomments
brandonbloom··on Go and Simplicity Debt Redux
1) You're not "abstracting away" anything like that. If you're talking about operations you can abstract over `a option`, that has nothing to do with nesting.

2) I would consider explicitly nested option types to be a code smell. If `foo option option` happens incidentally via a module functor or something, fine, but if you ever write that by hand, you probably want to explicitly flatten that to a 3 or 4 case sum type with explicit constructor names.

3) Nested structures like this are very unlikely to have synthetic, abstract names like MaybeMaybeFoo. Instead they are more likely to have operational, domain-specific, concrete names names. These structs are likely to _already exist_, since Go lacks type inference for function parameters: You're already going to have to create types in order to talk about these things.

4) You're "manually" packing/unpacking by writing (SOME (SOME ... constructor calls and in your pattern match. The difference between &Foo{&Bar{...}} and (Foo (Bar ...)) is irrelevant. Meanwhile, pattern matching has a syntactic advantage over a `== nil` check, but again, I'd argue that intentional nesting of numerous expected absent values is a code smell. If you really need that, you'd probably employ the null object pattern, which is easy to support in Go because methods can be called on nil instances. I'll say: I've never had to do that in tens of thousands of lines of production Go.

brandonbloom··on Go and Simplicity Debt Redux
> Are there any Java, C++, etc. developers arguing for the removal of these features from their languages?

I will. Generics in Java are a net loss in my opinion. They provide marginal static safety, no additional dynamic safety, no performance benefits, and lead to substantially more complex APIs. I'd have been happier if they were never added. I would prefer a Go-like type-assertion construct or an analogous "occurrence typing" feature.

C#, on the other hand, has a sensible generics implementation that provides meaningful utility thanks to value types. However, I am not ashamed to admit that, in the absence of value types, I'll resort to Whatever<Object> plus some casts without a second thought if I find myself doing even the slightest bit of work to satisfy the compiler.

brandonbloom··on Go and Simplicity Debt Redux
Huh? What kind of "nesting" are you talking about? How is that different than nesting structs containing nullable pointers?
brandonbloom··on Weight Training Is the Best Exercise for Bone Strength
Me too. Another vote for "Starting Strength" being a life changer.
brandonbloom··on Writing a Lisp: Continuations
I have implemented interpreters that support both full and delimited continuations. They are underused for many very good reasons. A continuation makes an implicit state machine. Implicit means anonymous, which means harder to talk about, which means harder to reason about. But there are technical problems too.

Continuation based sessions, as used in fringe web frameworks such as Seaside or even Arc's library that powers Hacker News, is a really problematic approach. Reifying a slice of call stack in to a continuation and serializing it introduces all sorts of problems for resource management and the user experience, especially in the presence of change.

For example, let's say you deploy a new version of your code: What happens to all the in-progress sessions? Do you let old code keep running? Do you force users to start over? Compare to "Edit and Continue" in a Java/C#/Smalltalk codebase where you can change a method and all instances get the new behavior, vs if you change a function that constructs another function: All the old closures stick around with the old code.

Let's say a user leaves a browser tab open for a while: How long do you wait before invalidating the session and free its resources? What impact does that have on the user experience? Hacker News has mostly eliminated its reliance on continuations to remedy these sorts of problems.

Speaking of resources: What resources does a continuation hold on to? If you serialize a continuation, do you keep all file handles open? Do you perform static analysis to know a priori what data should be garbage collected or excluded from the continuation as "semantic garbage"? This problem is double bad when laziness is involved. Consider paginating through a database cursor.

What happens when a continuation must be forked? Consider using a continuation-id to resume an interaction with a web app: Does opening a new tab duplicate any held resources by the continuation? Do your external resources even support cloning operations or are they thread safe? Compare to unix "fork" and file handles vs shared memory.

brandonbloom··on The Ultimate in Garbage Collection (1995)
Fun story!

The same idea applies more generally as well with so-called "region-based" memory allocation. A sub-process allocates incrementally from a pool, then when that process ends, the entire pool is freed at once. Optionally, garbage collection or other memory freeing can happen during the process. Additionally, regions can be arranged hierarchically for processes and sub-proesses, recursively.

The easiest way to take advantage of this is, if for example, you have a short-lived command line tool. Maybe you don't even bother to free any memory at all, as the operating system is going to free it all for you when you're done anyway.

brandonbloom··on Understand Go pointers
I think others answered the question of pointers vs not-pointers reasonably well, but your question implies you already understood that. The interesting thing to me is that they _did_ make some pointer types implicit: maps, chans, etc. Moreover, you can easily hide a pointer in a struct, such that the use site gives no hint as to whether or not you're going to have "spooky action at a distance" for mutations.

Given this, a more interesting question is "Why can't I define types that are implicitly always pointers?" I don't know the answer to that. My guess would be path-dependent design. As I understand it, even maps and chans were explicitly pointers for a long while in the early history of Go.

I've found that I can classify all structs in to two groups:

1) simple record values with all public fields; rare methods for convenience or specific polymorphism. 2) encapsulated machines with all private fields; only accessed through rich method interface.

Any blending of public and private fields has turned out to be a mistake in my experience. For record types, the implicit copying is valuable for passing data around. With encapsulated machines, I almost always want all usages to be a pointer. I wish I could define types like this:

    type MyMachine *struct {
        ...private fields...
    }
Which would basically compile like this:

    type machineState struct {
        ...public fields...
    }

    type MyMachine struct {
        *machineState
    }
One problem with this approximate macro-expansion is that you can't use "nil" for MyMachine, instead you need to use (MyMachine{}) as a zero value. I'd change that across the language too and just make nil a proper polymorphic literal zero value for any type.
brandonbloom··on Dale – A Lisp-flavoured C
1) That's an interesting project. Worthy of discussion on its own.

2) "Why isn't it written in Lisp?" It's 42% C++ according to GitHub.

3) Based on a hunch and past experiences, I'm going to ignore any follow ups on this thread.

EDIT: And now I look like a jerk because lispm replaced his terse comment with a fuller comment. My apologies for even engaging in the first place. Should have known better.

brandonbloom··on Dale – A Lisp-flavoured C
C-MERA's default code generator is textual C. This project uses LLVM's API, which is sadly difficult to utilize outside of C++.
brandonbloom··on Fourteen Months with Clojure
For what it's worth, I'm a pretty happy vim-fireplace user for evaluating files and forms. I also happily use standard vim brace matching, text-objects, etc instead of paredit or similar.

For JVM development, I only bother with NREPL because vim-fireplace needs it. For library development, `lein repl` gets the job done. For application development, I copy/paste around just enough code to launch an nrepl server and run the Clojure jar directly with a vendored directory of jar files. No other tools needed really.

brandonbloom··on Uber Driver Tip-Hustling Schemes
At least according to this, 4.7 is quite typical for passengers:

http://valleywag.gawker.com/we-asked-you-for-your-uber-score...

brandonbloom··on Uber Driver Tip-Hustling Schemes
> your cancel reflects poorly on you in Uber's rating system

This is simply not true. I just checked: My rating is 4.73. I've probably taken a few hundred rides with Uber. I assume they adjust ratings for bad experiences, especially if you're a regular rider.

By the way: It's not that hard to get consistent 5 stars as a passenger. Be respectful of the driver and follow these steps: 1) Say something like "Hi, are you looking for Brandon? That's me. Good evening. How's it going?" 2) Suffer less than 20 seconds of smalltalk 3) Go about playing with your phone or talking with your co-passengers quietly. 4) Say "This is my stop right there by the red car on the right. Thanks a lot, you have a good night!" - If you're an extrovert, you'd do this anyway (assuming you're not an asshole) and if you're an introvert, it's a good opportunity to exercise your smalltalk with low stakes.

brandonbloom··on Uber Driver Tip-Hustling Schemes
I'm only a little surprised that nobody has suggested this, but: If the driver does something shady, just get out of the car. Immediately. Don't even give them the opportunity to apologize for it.

I've had drivers call ahead to ask where I'm going. I just hang up on them, cancel the ride, then order another ride. While I'm in the car with a new driver, I report the incident using the in-app system and, on many occasions, they even grant me a credit!

Similarly, I've had on at least two occasions where the drivers did obviously stupid, unsafe things within the first 30 seconds of a ride. I ask them to pull over, and I just quietly get out of the car, report the incident, walk away, and order another driver.

brandonbloom··on Ask HN: Example of a 'non-scalar' programming language?
It's not easy, but it's possible. Check out FastR: https://github.com/graalvm/fastr

Here's a good video about the language and this implementation: http://download.oracle.com/technetwork/java/javase/community...

brandonbloom··on Ruby `reject!`
I meant O(N) space, as you said "That would still require O(n) space" - but a slide does not require additional space.
brandonbloom··on Ruby `reject!`
Because you're saving the O(N) time copy...

iirc, Ruby's array doesn't expose the distinction between length and capacity, but internally it may decide to amortize the cost of such copies. For example, if reject only removes a small % of elements, it may choose not to shrink capacity.

brandonbloom··on Ruby `reject!`
xg15's comment still makes sense if you replace "copy all ..." with "slide the remaining elements down by the number of removed elements" to match the mutative behavior.

However, a bigger problem is concurrent modification: What happens if your reject! predicate has a side effect which modifies the array? Ideally you'd receive a "ConcurrentModificationException" or the like.

brandonbloom··on There Are Three Programming Paradigms (2013)
Think about a mutable variable. In an imperative program X can start with A, then become B, then become C, etc. In a functional program, you can say that X doesn't change, instead there's really a subscript: X0=A, X1=B, X2=3. That is, you're projecting the time dimension, which is implicit, linear, etc, on to a space dimension: The name of the variable expands from ["x"] to ["x", 1] etc.

Monads are the (an?) ultimate exploration of this idea. You project the timeline of the entire world on to a space dimension. Each "bind" operation builds up a new world and you can have many forks of the world, as it's just a model, not the world itself. This model doesn't do anything until you "run" the monad by handing it off to some higher level interpreter.

As for constraint satisfaction, check out Sussman's talk "We Really Don't Know How To Compute" <https://www.youtube.com/watch?v=O3tVctB_VSU> - Even though they implement the propagator networks with a message queue and a single thread, you could in theory run each propagator in parallel, sending messages back and forth.

brandonbloom··on There Are Three Programming Paradigms (2013)
I can try, but I'm not just going to guess what your misunderstanding is. What specifically would you like me to expand upon?
brandonbloom··on There Are Three Programming Paradigms (2013)
It's no more about implementation strategy than compilation vs interpretation, which is also spatial and temporal in nature.
brandonbloom··on There Are Three Programming Paradigms (2013)
Logic programming really falls in to two main categories: search and constraint satisfaction. In that light, there's really only two programming paradigms: Temporal and Spatial.

Imperative programming is programming with time. Functional programming projects time on to space. When you add concurrency, either effectively becomes logic programming: Search runs multiple spatial processes in parallel. Constraint satisfaction treats each constraint variable as its own timeline.

We only don't think of it this way because we've traditionally linearized logic programs. You can linearize search with backtracking and you can linearize constraint satisfaction with a constraint propagation queue.

brandonbloom··on LG 5K display must be kept at least 2 meters away from Wi-Fi routers
I don't know where you got that impression. I have this LG 5K display and I'm typing on a keyboard connected to it right now...
brandonbloom··on Standing up for what's right
Why do you care? If getting credit as a "hero" is what it takes to get him to do good things, then let him have it. Even if this is a PR stunt, it has positive externalities that can only be amplified by adept public relations management.
brandonbloom··on Rust vs. Go
I don't necessarily disagree.

Although in the context of the simple vs easy notion, I suggest you grep this page for "inherent complexity":

https://github.com/matthiasn/talk-transcripts/blob/master/Hi...

He talks about the inherent complexity in resource contention.

I will point out one choice quote:

"It's making things complex because that's a decision that needs to be made by someone who has better information."

Rust addresses this by forcing you to highly parameterize code statically, which is probably the least bad solution we have right now for this sort of stuff in a low-level language.

brandonbloom··on Rust vs. Go
You're missing a key element from Rich's talk: Complexity occurs when things are complected. That is, when two separate concerns are tied together.

There's lots of this in Rust, especially in the form of premature specialization. If you define a struct or a method in Rust, you might be forced to make some decisions about lifetime, static vs dynamic dispatch, memory representation, mutability, etc.

These things are tied together deeply in order to create a tractable space in which the type solver may operate. Granted, this may also create a tractable space in which for you to think about these same issues, but that doesn't mean its any less complected/complex.

brandonbloom··on My Go Resolutions for 2017
And as I keep saying: I don't value those assurances and in fact find them to be a net loss when accounting for breaking changes changes to clients.
brandonbloom··on My Go Resolutions for 2017
Sounds like type-assertions cover that use case just fine. However, you are correct that you can't do this without allocating. ¯\_(ツ)_/¯
brandonbloom··on My Go Resolutions for 2017
I'm going to dodge the breaking changes discussion, since it's a huge tangent, suffice to say that I greatly dislike _breaking_ changes and want languages that make it possible to avoid them or at least provide a migration path via incremental deprecation prior to removal. Go fails here in many ways. All I meant to do was point out that exhaustiveness checks are not without their tradeoffs.

> I was visualising something like

OK then. We're in agreement on that point. This is what Clojure has in it's destructuring syntax, and, as I said, it's great.

brandonbloom··on My Go Resolutions for 2017
Go will give you errors for "impossible type assertions" in many cases.
brandonbloom··on My Go Resolutions for 2017
> very basic enum unwrapping based on types, which is compile time checked

Sooo just accessing fields on a struct? I don't understand the distinction you are trying to make.

← PreviousPage 4 of 16Next →