HNHacker News
TopNewBestAskShowJobs

sullyj3

1,004 karma · joined July 4, 2014

submissionscomments
sullyj3··on All you need is call/cc (2013)
One key difference is that usually the captured values in a closure can't be mutated.
sullyj3··on The world needs a proper investigation into how Covid-19 started
I hate to be be the one to tell you this, (just kidding, I get a little sadistic thrill out of it) but if you think the best way to defend science is to shy away from searching for truth, you may possibly have missed the entire point of the enterprise.
sullyj3··on Git password authentication is shutting down
Understanding the fundamentals of public key cryptography is a worthy pursuit. Memorizing the idiosyncratic flags of yet another command line tool (or looking them up in the man page every damn time like most people do), instead of having an interface that doesn't require you to relearn it every time you use it, isn't.
sullyj3··on Git password authentication is shutting down
> What do you expect? That people without prior knowledge or experience just pick up source control in seconds?

I mean, ideally, that'd be great. I don't think there's any fundamental reason things can't be this way, many of these complexities are completely incidental, rather than inherent.

> Is it really too much to ask a newcomer to invest some hours into learning something these days?

No, but when I'm learning something, I prefer for my time to be spent on important stuff, not wasted unnecessarily.

sullyj3··on Defunctionalization: Everybody does it, nobody talks about it (2019)
This seems like exactly the sort of issue the Unison language is trying to tackle. They want straightforward execution of distributed computations using the same language, with no friction across machine boundaries, no sacrificing composibility.

The article mentions:

> remote procedure calls with higher-order functional arguments would require serializing those functions, which is not easy to do safely or efficiently.

This is exactly what Unison is designed to do. Code is not stored as text, but is stored in a serialized AST form and identified by hash. Therefore, transmission between nodes is trivial. The language is still a work in progress, but it's making rapid strides.

Here's an explanation: https://www.youtube.com/watch?v=gCWtkvDQ2ZI

The part relevant to distributed computation is at 30:08, but the earlier parts go through the basics of how code is stored, and why this design choice was made, which might help with understanding what's going on.

sullyj3··on A Bestiary of Functions for Systems Designers
Maybe a rundown of the different strcpy variants or something
sullyj3··on Unison Programming Language
If you could give a concrete example of a problem to be solved, I could try to convince you that the method using Either won't actually be all that unwieldy.
sullyj3··on Unison Programming Language
For the moment, you have to create a function with the appropriate IO ability, and execute using the `run` command from inside the codebase manager. I don't think there's a way to create a standalone executable just yet.
sullyj3··on Things you can’t do in Rust (and what to do instead)
Agreed, I think calling them enums is an example of trying a little too hard to make the language seem comforting and familiar to c/c++ programmers at the expense of clarity.
sullyj3··on Parsix: Parse Don't Validate
It's true that here the data type was just a tag. There's always a decision to be made about how much structure you want to enforce by the construction of the data type. You'd still consider it to be "parsing" because you now statically know that an Email is valid according to the rules of the parser - in a loose sense, it's a "conversion" between a type you know nothing about, and one you know a lot about.

If you wanted to go further you could start going the "correct by construction" route - having a data type that enforces more invariants. For example, you might store the recipient name, domain name, and tld of your email in separate fields. Then your parser would more obviously be a parser. I think of the "mere tag" type as a kind of degenerate case of this, rather than something totally separate.

sullyj3··on Red and blue functions are a good thing
It's true that you can't extract a value obtained from the outside world from the IO type by design, but, I don't think I'd characterize that as a problem. For any piece of code someone thinks they want to write that involves turning an `IO something` to a `something`, I can show how to solve the problem without doing that with a minimum of fuss. It's actually quite difficult for me to remember why someone would want to do that, having been immersed in this paradigm for so long.

For example, if you have, say,

`getIntFromSomewhere :: IO Int`

and you want to apply a pure function of type `Int -> Int` to it. Supppose the pure function adds one. You might initially think that that's all there is to it, you don't want IO involved in what happens next. But of course, the only reason you would ever do some pure computation is to use the result to have some effect on the world, eg printing it to a console. So actually in a sense, you do want to stay in IO.

    myIOAction :: IO ()
    myIOAction = do
      myInt <- getIntFromSomewhere
      print (addOne myInt)
As you say, you can't pull the int out of getIntFromSomewhere without operating in an IO action. But why would we want to, since we obviously want to make use of that value to affect the outside world? And of course, that doesn't mean we have any issues getting that int into a pure function `addOne :: Int -> Int` first, before eventually (immediately, in this case) making use of the result to perform some IO.
sullyj3··on Red and blue functions are a good thing
I don't really know much about this area, but there's a language literally called Lawvere.

https://github.com/jameshaydon/lawvere

sullyj3··on Red and blue functions are a good thing
It's not entirely clear what you want to happen here with `f` and `g`. It's trivially easy to apply `g` to the `b` in the result of f. You just use `fmap`.

    -- suppose we have some x :: a
    fmap g (f x) :: IO [c]
Of course, you wouldn't want the [c] to be able to escape the IO container, because that would break referential transparency - you could no longer guarantee that a function returns the same result given the same arguments. Of course, that's no problem for manipulating the [c] value by passing it to pure code.

It's true that "monad's don't compose" in the sense that you need to manually write monad transformers to correctly combine monadic effects, whereas functors compose automatically. However, that's not the same thing as saying it's difficult to use functions which happen to return different monadic results together, if the types match.

sullyj3··on Open letter from researchers involved in the “hypocrite commit” debacle
There's no apology they could have written that would not result in some people saying it was insincere.
sullyj3··on Lambda: The Ultimate Excel Worksheet Function
This demonstrate countability, not finiteness. If by "world" you mean universe, there's no guarantee that you can "take everything in it", because it might be infinite.
sullyj3··on Software Infrastructure 2.0: A Wishlist
The point about the AWS console being thoroughly un-delightful is well made, it's a nightmare.
sullyj3··on Ask HN: Good Sources of News in Australia?
https://www.michaelwest.com.au
sullyj3··on Amp: Vi-like terminal editor written in Rust
Yeah, I'm a neovim user, and I enjoy that feature. There are just times when multiple cursors would be ever so slightly more ergonomic.
sullyj3··on Illegal Prime Numbers
Copyright may or may not be a good idea, but arguing its merits isn't relevant to the question of its current legality.
sullyj3··on Amp: Vi-like terminal editor written in Rust
Very cool, has some interesting ideas!

Things I love:

- Many common operations are a single keystroke - saving, switching buffers, opening a new buffer. Simple design choices like this make the editor feel incredibly fluid.

- Jump mode is very cool

- Using interactive search for commands and opening files

Things that I miss most coming from vim:

- text objects

- paragraph motion.

- Jumping back to the previous cursor location - often useful after a search.

Overall, I think the decision to neglect almost all motion commands in favor of focusing solely on jump mode, w/b and hjkl, is mistaken. While f is undeniably fast for many operations, there are cases where it performs terribly.

Suppose I want to move a function somewhere else. In vim, I'd probably do something like `{d}` - move to the start of the paragraph, delete til the end of the paragraph.

By contrast, Amp's jump mode kind of implicitly assumes I want to be on the start of some word. It's difficult to quickly jump to an empty line. I have to hit f, enter the character pair for the first target in the paragraph, then hit k. Then I go v, hit f again, go to the final target in the paragraph, press j, and finally hit d. That's a 3 character operation that can be performed with muscle memory, to a 10 character operation that prompts the user for input twice, both times forcing them to slow down to visually locate a point near where they actually want to go. That's a pretty massive useability regression. It'd be good to see some better navigation options for use cases like this.

sullyj3··on Amp: Vi-like terminal editor written in Rust
The installation dependency list is missing libxcb.
sullyj3··on Amp: Vi-like terminal editor written in Rust
That's definitely a killer feature for me.
sullyj3··on Amp: Vi-like terminal editor written in Rust
Agree about the second one, that seems weirdly specific, but , and m for scrolling seems reasonable enough and p/P for pasting is the same as the vi family.
sullyj3··on Amp: Vi-like terminal editor written in Rust
The difference is the interactivity and instantaneous feedback. I don't want to have to blindly write a regex substitution when I could instead be seeing what is happening at each step, right in front of me.
sullyj3··on Bloom filters explained in a single image
Ah, thanks haha.
sullyj3··on Bloom filters explained in a single image
What's the intuition behind the name?
sullyj3··on What Is the Switch-Case Equivalent in Python?
> How is case different from the elif pattern?

Pattern matching is often conflated with switch-case by people who are unfamiliar, because pattern matching subsumes switch-case, but they're very much not the same thing. Structural pattern matching is much more powerful than checking a series of boolean conditions. It allows you to not only check the structure of a value, but bind parts of that structure to variables. It allows you to express logic that would would otherwise be fairly complex in a straightforward and transparent way.

sullyj3··on Golang: Know Your 'Nil'
1. You can use that style as well. You're free to return early in the error arm of the match, and make use of the Ok value in later straight line code. I've done that in fallible_function in this example:

    fn main() {
        // prints "first call worked"
        if let Ok(i) = fallible_function(Ok(1)) {
            println!("first call worked");
            
        }
    
        // prints "second call failed: FallibleError("error!")"
        if let Err(e) = fallible_function(Err("error!".to_string())) {
            println!("second call failed: {:?}",e);
        }
    
    }

    #[derive(Debug)]
    struct FallibleError(String);

    fn fallible_function(x: Result<i32, String>) -> Result<i32, FallibleError> {
        let y = match x {
            Err(s) => { return Err(FallibleError(s)); },
            Ok(i) => i,
        };
    
        // y now contains the i that was in the Ok.
        // do straight line code with y here
    
        Ok(y)
    }
2. You can create custom errors for a specific function, and put any data that you would have passed to Errorf inside. This way you get the ability to introspect errors to see what went wrong programmatically, and all that data is available for later inspection. Note that we could also have returned a formatted string on error instead of FallibleError exactly like in Go if we wanted to.

Of course, the way you'd write fallible_function if you weren't going out of your way to be verbose would be like this:

    fn fallible_function(x: Result<i32, String>) -> Result<i32, FallibleError> {
        let y = x.map_err(|s| FallibleError(s) )?;
        // y now contains the i that was in the Ok.
        // do straight line code with y here
    
        Ok(y)
    }
Separately, the point of all this is to be able to statically know whether a function can fail or not. We know for a fact that fallible_function can fail. If we write a function

    fn f(x: i32) -> i32 { .. }
We know for a fact it won't fail (unless it panics, but well behaved code should never panic). We don't even have to worry about the possibility of nils getting in there and screwing us up.
sullyj3··on Golang: Know Your 'Nil'
Yeah, I'm familiar with the ML derivatives, I'm a haskeller, I was musing specifically about imperative languages, because I doubt go programmers would be interested in that much of a switch up.
sullyj3··on Golang: Know Your 'Nil'
Complexity around memory management and lifetimes is a definite tradeoff that rust makes. A GC imperative language that uses Sum/Product types seems interesting. I guess that would be swift? I'm not familiar.
← PreviousPage 4 of 18Next →