Hacker News new | past | comments | ask | show | jobs | submit | tyoverby's comments login

> What would Target do if their canned beans vendor started putting labels on the cans like "we sold this item for $0.23. Anything else is profit that your store is charging you."? Those cans would never see the shelves.

Arizona Iced Tea does this and they're very successful.


Arizona Iced Tea puts the suggested retail price on their cans. They do not disclose what price they charge to the retailer on the cans.


Yeah but the implication is you should never pay more than 99 cents or whatever. Coke did the same thing, running advertisements about 5 cent Coke even though they couldn't control the actual price of a bottle of Coke. Worked really well apparently.

Also, I've never paid above 99 cents for Arizona. So it seems that worked as well.


Books have printed the recommended price on the cover for decades. So have many snack foods.

It's very different than disclosing your revenue split on the packaging. Apple lets you set the price on your app store page. And no one will have to pay more. They would object if you mentioned your revenue split there.


OCaml has an incredible optimizer, and many of the abstractions that one would use in OCaml are transparent to the optimizer.

You could say the opposite about Go.


Kinda makes me wonder why OCaml didn't become more popular for Unix systems programming, the way Go seems to be becoming now. Looking at the two languages, it seems like pretty much everything that Go has to offer in that department, OCaml has as well - and if it also optimizes better, why is this even a contest?


Human Chess players lost against Deep Blue in 1997; people still play Chess. In fact, they've learned a lot of strategies from the strategically superior AI.


Here's an example scenario:

1. Your PR is tested with the merge against Master. All green!

2. Another PR is tested with the merge against Master. All green!

3. The other PR goes in first. All good!

4. Your PR goes in (merge happens, but introduces a bug). Oh no!


Thanks! Someone pointed it out in a sibling comment afterwards too. Kind of sucks, I wish they had a "do final check and merge" button that would merge PRs sequentially into master after re-testing when there's a collision like this. Any idea why they don't?


I think this is more of an issue with GitHub not firing another webhook, than Travis. Travis just does what it's told.


For Microsoft at least, most of my coworkers send their kids to public school.


I work on the C# compiler and the answer over in that world is "no"; everything needs to be supported.


I've been playing around with delimited continuations in a toy programming language of mine for a while now and I seriously think that its a paradigm that will eventually take over the scripting language space. First-class control flow is so powerful once you get the hang of it!


they already exist even though in a limited form: async/await, coroutines, generators etc. I doubt whether first-class support is that important.


"First class functions exist in a limited form: Function pointers, etc. I doubt whether first-class support is that important".


Well, i would love to see some applications of delimited continuations other than those mentioned here: http://matt.might.net/articles/programming-with-continuation... . Most languages have support for these applications.

The only thing other than that i have seen work is continuation based web servers.


The page that you linked to shows off some features that are not really found in any major programming langauge. Time-traveling search is a big one for me.

Many languages have exceptions, but with shift/reset, you can implement your own exception handling behavior trivially, including things like resumable exceptions.

Some languages have async/await, but almost all of those implementations are state-machine based, which means that they don't compose well when used with more complex language features like generics and higher order functions.

But more than that, in continuation-based asynchronous programming, "async" functions are just normal functions that execute in an async prompt. Here's an example of a multi-user asynchronous TCP echo server in my toy language with tagged delimited continuations:

    async {
      loop {
        let conn = accept_connection("localhost", 9999)
        async {
          loop {
            let data = conn.next_data()
            conn.send(data)
          }
        }
      }
    }
Where all of those asyncrhonous constructs (including `async` itself) are really short wrappers around delimited continuation machinery.

You'll notice that the nested calls to `async` indicate that there are two asyncrhonous contexts

1. Connection acceptance happen on their own "thread" of control

2. Each connection gets their own context in which everything is internally asyncrhonous


time-travelling search can be done with recursion. Otherwise, i agree that they don't compose well with each other in some languages. However, it is harder to implement efficiently as far as i understand; time will tell if they are widely understood to be implemented in future languages.


Another thing that I should mention is that most langauges, features like generators / async / exceptions don't compose well at all. In a language where these features are implemented through continuations, it's really easy to pick how they interact, leading to some really powerful tricks!


Could you list some these (main stream) languages. I know a few but I am surely missing the others you know of.


As hinted at in this paper, continuations are directly at odds with dynamic unwinding (e.g. "finally" blocks).


> I think the generalization of "use-after-free" to "use-after-invalidation" is the most important

> I wonder if this sort of vulnerability is possible in Rust

Rusts borrow checker is designed for the more general case of "use-after-invalidation" and `free` is treated as a simple invalidation of what happens to be a heap allocated structure.

Interestingly, the borrow checker also prevents invalidations that are still common in memory-safe languages such as iterator invalidation.


With good design, the range of invalidations that Rust can prevent go well beyond memory invalidation. For example, a serialization/deserialization library I'm working on has the following trait:

    pub trait Decode : Sized {
        fn decode<D: Decoder>(decoder: D) -> Result<(Self, D::Done), D::Error>;
    }
Basically, types that can be decoded implement this trait.

The trick here is that the decode() method consumes the decoder, and then returns it in the output. In the case of decoding an IO stream, the D::Done type is the IO stream itself, which means that in the event of an error, we ensure that the user can't accidentally use the IO stream again in an incompletely decoded state because all they have is the error type, D::Error (they can intentionally use the IO stream again by recovering the IO stream handle from the D::Error type).

In practice, the above results in decode() implementations that look like the following:

    fn decode<D: Decoder>(decoder: D) -> Result<(Foo, D::Done), D::Error> {
        let (v0, decoder) = decoder.decode()?;
        let (v1, decoder) = decoder.decode()?;
        let (v2, decoder) = decoder.decode()?;
        Ok((Foo(v0, v1, v2),
            decoder.done()?))
    }
This is a bit more verbose, but as I also make use of Rust's procedural macros you'd also never actually write the above code; it's auto-derived/auto-generated for you 99% of the time. Equally, if I ever do make a mistake in the auto-generation this state-machine-like approach makes it very likely that the resulting auto-generated code won't even compile.


In this example, though, the library's C++ sort is calling out to Javascript code during the sort. That code can alter the object being sorted. That's where the trouble comes from. The sort function needs exclusive mutable access to the object being sorted. But Javascript doesn't support such access control.

This is a general problem with cross-language data access. The languages may not have the same data model. It's especially bad when one side has garbage collection, and the other side has to have explicit GC-aware code.


What? It's very easy to get use-after-invalidation in Rust. Destructors called during unwinding see stuff in an invalid state. You can probably make a language that prevents use-after-invalidation in safe code (e.g. mark all accessible mutable references as "dirty" during unwinding, and require unsafe code to "clean" them) but Rust isn't trying to do that AFAIK.


Could you provide an example of such code? I was under the impression that certain things were disallowed because destructors aren't allowed to see the struct in an invalid state.

A common case where I see people trying to do this is when you have a struct where you are trying to replace a member variable:

    struct Foo {
        thing: Vec<i32>,
    }
    
    impl Foo {
        fn something(&mut self) -> Vec<i32> {
            let temp = self.thing;
            // If we panicked between these two lines, then the struct would be in an undefined state
            self.thing = vec![1];
            temp
        }
    }
This code produces the error `cannot move out of borrowed content`. For those curious, you normally would write this as

    use std::mem;
    
    impl Foo {
        fn something(&mut self) -> Vec<i32> {
            mem::replace(&mut self.thing, vec![1])
        }
    }


How about this?

    struct Foo { bar: Bar }
    struct Bar { message: &'static str }

    fn change_foo(foo: &mut Foo) {
        change_bar(&mut foo.bar);
    }

    fn change_bar(bar: &mut Bar) {
        bar.message = "Invalid";
        if true {
            panic!();
        }
        bar.message = "Valid";
    }

    impl Drop for Foo {
        fn drop(&mut self) {
            println!("{}", self.bar.message);
        }
    }

    fn main() {
        let mut foo = Foo { bar: Bar { message: "" } };
        change_foo(&mut foo);
    }
The destructor of a struct sees a broken invariant of a nested struct.


I see, I think you are using a different definition of "invalid" than I and the grandparent are. Rust will not allow you to access memory that is invalid, but your own invariants can certainly be broken.

For what it's worth, the solution I've seen for this type of case is another struct that is used to restore to an acceptable state:

    fn change_bar(bar: &mut Bar) {
        let mut restore = Restore(bar);
        restore.message = "Invalid";
        if true {
            panic!();
        }
        restore.message = "Valid";
        // Don't rollback on success
        std::mem::forget(restore); 
    }
    
    struct Restore<'a>(&'a mut Bar);
    
    // Put back to an acceptable state
    impl<'a> Drop for Restore<'a> {
        fn drop(&mut self) {
            self.0.message = "Restored to some state";
        }
    }

    // Sugar so we don't have to know about the wrapper
    impl<'a> std::ops::Deref for Restore<'a> {
        type Target = Bar;
        fn deref(&self) -> &Bar { self.0 }
    }
    
    // Sugar so we don't have to know about the wrapper
    impl<'a> std::ops::DerefMut for Restore<'a> {
        fn deref_mut(&mut self) -> &mut Bar { self.0 }
    }


Let's say that you and I both create a library. You try to publish your C++ library to the standard debian repository, and I'll try to publish mine to cpam, npm, pip, etc...

If you actually manage to get your library included, then we can compare how long it takes to update your library.


Systems like Debian, though, have the ability to add custom repositories. So after the initial, possibly small package that just configures the sources.list, everything's handled with all the other updates. That's something that npm, etc, can't do.


Npm and many other package managers support custom repositories just fine


Sure. I publish to deb repos all the time.

(I've found Bintray to be the easiest.)


I didn't say "a" deb repo, I said "the standard debian repository". Just like when I'm talking about NPM, I'm talking about the main NPM repository, not my company's private NPM server.


Well, you've chosen a terrible comparison then.

How many NPM repos are there? Just one, too-big-to-fail, centralized repository.

Don't ding apt/deb just because people setup their own repos rather than put all the code in the world into one bucket.

There's a lot more inconvenient things than adding the /etc/apt/sources


> How many NPM repos are there? Just one, too-big-to-fail, centralized repository.

There are actually plenty, it's just that they're usually found on-prem at a company.


It was the last unit in senior math at my high school.


Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: