C Macro Magic - <AUTHOR> http://sagartewari01.com/c-macro-magic/ <comment><USER2> This is exactly how the list implementation in the Linux kernel works [1]. I've extracted this header file and used it in user-space before, too. The cool trick is in calculating an offset to the start of the struct containing the link.

But I do not think this trick or list implementation is newly introduced in Wayland. It existed at least as far back as kernel 2.6.11 [2].

[1] [https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/list.h?h=v5.0-rc8)

[2] [https://elixir.bootlin.com/linux/v2.6.11/source/include/linu...](https://elixir.bootlin.com/linux/v2.6.11/source/include/linux/list.h)

<comment><USER3> I've seen this same trick done with macros in BSD kernel code from the 80s!

<comment><USER4> Here's the code from FreeBSD: [https://github.com/freebsd/freebsd/blob/master/sys/sys/queue...](https://github.com/freebsd/freebsd/blob/master/sys/sys/queue.h)

</comment></comment></comment><comment><USER5> > Maybe that's the reason people who use the C language love it so much. You > can't do that in any other language.

But you can't do it in C. __typeof__ is a GNU extension.

> Note that wl_list is not a very safe data structure, in the sense that a > programmer need to know exactly how to use it.

You can do it in C++ with template, and it would be type safe.

<comment><USER6> typeof is a GNU extension, but it (or an equivalent) is implemented in every compiler anyone actually uses.

You can do it in C, you just can't do it in the part of C the standards committee has decided to standardize.

I'm not sure what the value is of defining C as standard C when loads of software has shipped for decades using this pattern.

<comment><USER7> And there you have it. Purists claim C or C++ can't do X because it's UBH in the standard. Pragmatists know that C and (especially) C++ aren't languages on their own, while "gcc" and "MSVC++" are complete (and very similar) languages.

<comment><USER8> And careful people keep in mind that only what's defined in the standard can be expected to be true across compiler updates.

<comment><USER7> Good clarification, you really only know what you're going to get on (language, compiler, compiler version, platform).

<comment><USER8> You also need to know the exact implementation of your compiler's optimizer, otherwise you don't know whether it's safe to change your program. Any change might give the optimizer enough information to unleash the nasal demons in previously cromulent code.

<comment><USER7> And it's stuff like this which is making me increasingly uncomfortable with using C++ as my main language. Currently it looks like Rust is the most suitable replacement but I'm not sure if it's there yet.

</comment></comment></comment></comment></comment></comment></comment><comment><USER9> A comparable implementation in Zig. Key detail is `@fieldParentPtr` [1], which has the same functionality as `container_of`.

        const std = @import("std");

        const wl_list = struct {
            prev: *wl_list,
            next: *wl_list,

            fn init(list: *wl_list) void {
                list.prev = list;
                list.next = list;
            }

            fn insert(list: *wl_list, elem: *wl_list) void {
                elem.prev = list;
                elem.next = list.next;
                list.next = elem;
                elem.next.prev = elem;
            }
        };

        const Data = struct {
            data: i32,
            link: wl_list,

            fn init(data: i32) Data {
                return Data{
                    .data = data,
                    .link = undefined,
                };
            }
        };

        pub fn main() void {
            var foo_list: wl_list = undefined;
            foo_list.init();

            var e1 = Data.init(1);
            var e2 = Data.init(2);
            var e3 = Data.init(3);

            foo_list.insert(&e1.link);
            foo_list.insert(&e2.link);
            e2.link.insert(&e3.link);

            var entry = @fieldParentPtr(Data, "link", foo_list.next);
            while (&entry.link != &foo_list) : (entry = @fieldParentPtr(Data, "link", entry.link.next)) {
                std.debug.warn("{}\n", entry.data);
            }
        }

[1] [https://ziglang.org/documentation/master/#fieldParentPtr](https://ziglang.org/documentation/master/#fieldParentPtr)

</comment><comment><USER10> 2 things concerning wl_list:

1) In C++ you can use struct inheritance to get the same functionality without macros or templates. ie: struct ThingList : wl_list { /* data elements */ }

2) Like in the C++ case above, the next and prev elements should probably be at the top if you plan on iterating a lot since the next/prev pointers will be in the first cacheline that you load when you dereference previous list elem. In this case, it's also worth allocating list nodes with posix_memalign.

OTOH, there might be reasons why you want the list buried deep in the struct, like if the other data element access times are more important.

<comment><USER11> You can do something similar in C - make struct wl_list the first member of struct data, and just cast the wl_list node pointer to a data pointer (offsetof is zero).

I think the most common reason for this generic-location offset-of style is so that the struct data can have _multiple_ list nodes and be on _multiple_ lists (without any extra allocation). This is pretty common in the kernel.

</comment></comment><comment><USER12> Intrusive lists. A trick probably as old as C itself. Multiple libraries provide an equivalent in C++. The claim that you can only do that unsafely or need C "magic" is completely wrong. The safe implementations are not even slower, as the main gain is node cache locality.

</comment><comment><USER13> FWIW, this magic is how the Linux kernel implements generic linked lists, hash tables, and red-black trees:

[https://stackoverflow.com/questions/15832301/understanding-c...](https://stackoverflow.com/questions/15832301/understanding- container-of-macro-in-the-linux-kernel)

<comment><USER14> So this would be hashing the pointer? In which case it doesn't support keys, like strings, etc. I'm now wondering if this is the more common use case for hash tables, where you just need a reference to a unique object rather than a specific key.

<comment><USER13> No, that's not true. The insert function (hash, rbtree, etc.) takes a hash node as an argument. So, in the kernel implementation the caller is expected to hash.

EDIT: check out some examples to see for yourself: [https://elixir.bootlin.com/linux/latest/ident/hlist_add_head](https://elixir.bootlin.com/linux/latest/ident/hlist_add_head)

</comment></comment></comment><comment><USER15> Could be a catch that while this kind of constructs work OK for local lists, you have to be aware when passing the struct between applications or processes compiled with different packing but using the same struct definition with non- aligned members.

struct foo { uint8_t data[3]; struct wl_list link; };

For an application _link_ member may be at offset 3 while for another it would be at offset 4.

That may be the reason _link_ should be put as the first member, and a simple cast would let you walk the list.

<comment><USER12> Usually it does not matter as you keep items of the same type or know the type and exact compiler.

Unless you want to write them somewhere or put it as part of API and ABI. Which you probably shouldn't.

<comment><USER15> It's true that _types_ has something to do with it, but the main issue is padding. While I can keep the same type (i.e. _uint8_t_ ), it's the padding following _uint8_t var[3]_ that may vary according on packing options when compiling.

[http://www.catb.org/esr/structure- packing/#_structure_alignm...](http://www.catb.org/esr/structure- packing/#_structure_alignment_and_padding)

</comment></comment></comment><comment><USER16> Spelling: "it's" should be "its" except when short for "it is" I believe. i.e.

>It's a simple doubly linked list. Here's it's definition

The first one's right, the second should be _its_.

Though almost most native speakers seem to do this, so I guess it won't be 'wrong' for much longer...

<comment><USER17> FYI some people were taught to use an apostrophe to imply ownership. It used to be the "official" way, but no longer.

The gorilla guards it's bananas.

<comment><USER16> >It used to be the "official" way

Oh right, thank you, I didn't know the history. Short version: [https://www.merriam-webster.com/words-at-play/the-tangled- hi...](https://www.merriam-webster.com/words-at-play/the-tangled-history-of- its-and-its)

Did you mean, some people now alive were taught that "it's" is right (in "it's definition"), or just ownership generally (Harry's). Well, it's better than using apostrophes for plural's, which seems very common too.

</comment></comment></comment><comment><USER18> As others have pointed out, this is also used in the Linux kernel. But unfortunately it shares the kernel error of using the same type for list head and member. That's not good separation, and on particular it means list_add() is ambiguous (the kernel one is member, head, which is backwards).

Hence I recommend kernel refugees start with CCAN's list: [http://ccodearchive.net/info/list.html](http://ccodearchive.net/info/list.html)

</comment><comment><USER19> why is this amazing? such macros have been around since the 80s. then we got various implementations of intrusive list in C++ and they became typesafe

<comment><USER20> Because most people haven't seen it before? For people who program in languages in which this stuff isn't possible, this pattern isn't obvious. No need to dismiss it just because it's obvious to you.

</comment></comment><comment><USER21> C is surprisingly complex when you dig into it - I have a little example I pull out every once in a while where you can write code that runs one way when a global typedef is defined with a certain name, and other way when the variable isn't defined (compiling just fine both ways).

<comment><USER20> No C is very very simple. That's why it's not easy :)

No but really, there isn't anything to it. Just think about how a CPU sees memory and registers and it becomes intuitive. But that doesn't make it easy to use because it doesn't give you any guard rails to show you the one true way to accomplish a task.

<comment><USER21> I agree partially - overall C's promise was that it was a light wrapper over top of registers and memory, absolutely.

The issues with C are around some of the weirder bits of the parsing process, namely the context-sensitive parser and the "interesting" corner cases of how the C preprocessor works.

Edit:

Here's a fun example - try it with and without the typedef line commented out:

      typedef int T1;

      int main() {
          const T1 (*T1);
          printf("%d\n", _Generic((T1), const int *: 1, const int (*)(int *): 2));
      }

<comment><USER22> I think C has broken that promise since. It now targets an abstract machine, and a lot of behavior that would make sense for a light wrapper compiles to something counter-intuitive in an optimizing compiler.

In a different time, that something was left undefined by the standards might have meant that the result might be some artefact of the target machine architecture, but with aggressively optimizing C compilers the undefined behavior is more likely to be an artefact of the optimizer.

A thin light wrapper wouldn't compile this:

        static int doub(i) { return i + i; }

        int main(void)
        {
         int i;
         int sum;

         sum = 0;
         i = 5;
         while (i--)
          sum += doub(i);
         return sum;
        }

into this:

        movl $20, %eax
        ret

I'm not programming memory or registers, I'm instructing an optimizer.

<comment><USER23> What compiler does that? None of the ones I tried on god bolt seem to (after fixing the missing type on the function parameter)? And what's the undefined behaviour here?

<comment><USER22> I genuinely forgot to specify the type of the function argument, but that's not considered an error in C which will default to int when the type is omitted. If the compiler you're using objects to that it's probably for the better, but it's still legal C.

There is no undefined behavior in the example. It's meant to further illustrate my point that C is not a light wrapper around memory and registers. In the example, it's just a deceptively imperative looking way of describing the return value to the optimizer.

The compiler in this case was gcc with -O3.

For an example of behavior that is straight forward in the machine but is undefined in C, try signed integer overflow. Intuitively on x64 it should behave just like ADD, possibly adjust the carry and overflow flags and wrap around. In C, the behavior is entirely undefined. Some operation that results in UB is low hanging fruit for the optimizer. If it can deduce that the operation will result in UB, in this case signed integer overflow, it might omit the operation altogether.

</comment></comment></comment></comment></comment></comment><comment><USER24> An alternative could be to guarantee that the first two elements of the data structure are next and prev pointers. This way the implementation can just assume the first 8 or 16 bytes (depending on sizeof(void*)) are the next/prev addresses. Then the same set of functions can work with everything that looks like a linked list. Another option is to have a "struct list" that contains the information needed, and the API allows, when you initialize the list, to say how much space you want starting at "node->data". This way the node is always over-allocated by the library, and when you want to manipulate the list items you cast node->data to your privata data structure.

<comment><USER25> This approach is less type-safe.

<comment><USER24> Yep.

</comment></comment></comment><comment><USER26> > You can't do that in any other language.

Except languages with parametric polymorphism, such as ML and Haskell.

<comment><USER27> Not quite right, in other languages you (a) can't, and (b) don't have to (thankfully).

The compiler doing my offsetting for me using type information is much safer and more reliable.

</comment></comment><comment><USER28> Rust magic: [https://github.com/Amanieu/intrusive- rs](https://github.com/Amanieu/intrusive-rs)

</comment><comment><USER29> This is C's version a Ruby-style Mixin. By adding a List link to your datatype, you can use List operations on your data.

[https://en.wikipedia.org/wiki/Mixin](https://en.wikipedia.org/wiki/Mixin)

Also, is this _macro_ magic, or simply memory layout magic? The macros look like appreviations for readability, not magic required to make the structure work.

</comment><comment><USER27> An alternative implementation is to put the link structure at the beginning of the data structure. Then converting between one and the other is just a cast between the two types. This is a fairly standard technique in C to get some version of inheritance of structs, and how I have implement LLs in the past.

I don't see what this extra complexity of sizeof() offset calculations gains you.

<comment><USER30> More importantly offsetof is an implementation-specific macro. Whatever it does cannot be generalized from its definition for one or many compilers to a language requirement. The use of the offsetof macro in a program may cause undefined behavior when compiled with GCC or Clang. GCC instead provides __builtin_offsetof. If multiple lists are desired, then an array of them should be declared at the head of the struct and the index be fixed to the type of list desired to contain a reference to the struct. Additionally, as rustyrussell wrote in another comment the list head and member structs should be different in order to optimize the list struct as CCAN's list is implemented, but it too uses offsetof, which leads to the same issue of undefined behavior.

Ref:

[https://en.wikipedia.org/wiki/Offsetof](https://en.wikipedia.org/wiki/Offsetof)

[https://stackoverflow.com/questions/26906621/does-struct- nam...](https://stackoverflow.com/questions/26906621/does-struct-name-null-b- cause-undefined-behaviour-in-c11)

<comment><USER27> This is not true. offsetof is defined in the C standard. See [http://www.open- std.org/jtc1/sc22/wg14/www/docs/n1256.pdf](http://www.open- std.org/jtc1/sc22/wg14/www/docs/n1256.pdf), Section 7.17, point 3 on page 254.

I prefer not to use it due to the loss of type information and the potential for screwups, but you _can_ write ANSI portable C using it.

<comment><USER30> The Offsetof macro is vaguely "defined" in the C standard, but the implementation of the macro is not explicit. So, I do not understand why the original comment is not true?

<comment><USER27> The standard I linked describes both the type signature, and also what the macro should _do_. There are various ways that that can be implemented by the compiler, but assuming that you are using a compiler that meets the ANSI C standard, then the macro will behave as the standard says it should.

Using __builtin_offsetof _is_ undefined, because it is not standardised. The fact that __builtin_offsetof is used to implement offsetof by GCC does not affect the fact that offsetof itself is standardised (in the same way that a given internal heap operation is not standardised, but might be used by GCC to implement malloc which is).

</comment></comment></comment></comment></comment><comment><USER31> This implementation of doubly linked lists is not even exclusive to Unix-based operating systems, but also known as struct LIST_ENTRY under Windows/ReactOS and heavily used for kernel development there. They also provide a singly linked list version as struct SINGLE_LIST_ENTRY.

Check e.g. [https://docs.microsoft.com/en- us/windows/desktop/api/ntdef/n...](https://docs.microsoft.com/en- us/windows/desktop/api/ntdef/ns-ntdef-_list_entry) and [https://git.reactos.org/?p=reactos.git&a=search&h=HEAD&st=gr...](https://git.reactos.org/?p=reactos.git&a=search&h=HEAD&st=grep&s=LIST_ENTRY)

</comment><comment><USER32> Macro-based, non-intrusive data structures: [https://gitlab.com/oliver117/ucds/tree/master/src](https://gitlab.com/oliver117/ucds/tree/master/src)

</comment><comment><USER33> These sorts of macro tricks are cute, but they quickly become a pain when using an FFI.

</comment><comment><USER34> Oh but this is only the beginning. With boost MPL, you can do full metaprogramming _at preprocessing time_ (I'm not 100% sure but I think the C++ preprocessor is compatible with that of C? So that would make MPL applicable to C code as well.). You can generate code by iterating over lists of preprocessor 'variables' and do other crazy things that seem impossible the first few times you think about it. It's great, albeit a little bit confusing the next time you come back to it, because it makes you think more about what level you're metaprogramming at.

<comment><USER35> I think you mean the Boost Preprocessor Library. MPL is template based and quite a bit dated by today's standards.

C++ Preprocessor is pretty much the same as C (they might go formally out of sync from o e standard release to another but in practice compilers implement the same features as far as I know).

</comment></comment><comment><USER36> wl_list seems like an incomplete re-implementation of the LIST_ macros in <sys/queue.h>.

[http://man7.org/linux/man-pages/man3/queue.3.html](http://man7.org/linux/man- pages/man3/queue.3.html)

[https://github.com/freebsd/freebsd/blob/master/sys/sys/queue...](https://github.com/freebsd/freebsd/blob/master/sys/sys/queue.h)

<comment><USER37> It isn't, at least directly. I am actually really happy to see this alternative, as queue.h uses anonymous structs, which are C11 and not well supported.

</comment></comment><comment><USER38> I've used uthash in the past for a similar hash implementation. It certainly has its wrinkles, i.e. you may need to include several if you want to include an object in multiple hashes... but it was fun to use and straightforward! [https://troydhanson.github.io/uthash/](https://troydhanson.github.io/uthash/)

</comment><comment><USER39> From Objective-C, you can make switch statements support strings with 6 lines of macro:

      ifndef TTD_SWITCH_STRING
      #define TTD_SWITCH_STRING
      #define TTD_CASE(str)                       if ([__s__ isEqualToString:(str)])
      #define TTD_SWITCH(s)                       for (NSString *__s__ = (s); ; )
      #define TTD_DEFAULT
      #endif

<comment><USER40> Somebody, please just fix the language. Extend all the free compilers or, better yet, run the changes through the standards committee. People have been dying for this feature for over half a century.

While you're at it, just make the feature generic. Handle arbitrary arrays, structs, unions, and floating-point types.

The ... feature supported by gcc is also a huge usability improvement. Don't allow weird sorting order issues with strings. Don't allow it for structs and unions.

<comment><USER41> None of those types can be compared directly in C, so that's not making the feature "generic", it's making it magically super-powered. Associating comparison intelligence with arbitrary types is not very obvious how to do in C, but I guess the new super-switch could accept a function pointer in addition to the data, or something. Like:

        const char * s const = "bar";

        switch (s, strcmp)
        {
        case "foo":
          printf("I got foo\n");
          break;
        case "bar":
          printf("nopes, I got bar\n");
          break;
        default:
          printf("neither foo nor bar :/\n");
        }

Which would be fine/acceptable, but not exactly how C tends to roll.

<comment><USER42> Why not just use an enum for the switch, and if strings need to reflect the enum, use an xmacro to expand the enum into an array of strings?

[https://github.com/glouw/andvaranaut/blob/master/src/Theme.c](https://github.com/glouw/andvaranaut/blob/master/src/Theme.c)

<comment><USER40> That doesn't seem to have anything to do with the problem.

The problem: You have a string. (pointer to char with NUL termination) Depending on what that string is, you want to run different code. You want to do this with high performance and a minimum of fuss. No, it isn't an option to say "but what if I used an enum instead?". You have a string. A string is what you have.

Currently in C, you must choose between high performance and a minimum of fuss. Pick one.

The high-performance solution is to use bsearch or a perfect hash, mapping the string to an index of some sort. With purely standard C you would then use a normal switch. Using gcc extensions, you could get slightly better performance with a computed goto. Writing and maintaining this code is a pain, so most people don't bother.

The minimum-fuss solution is a whole bunch of "else if ... else if ... else if ..." that tries each possibility in turn. The performance is terrible, but good enough for stuff like command line option parsing.

</comment></comment></comment></comment></comment><comment><USER43> I avoid anything magic in C.

</comment><comment><USER44> Just because you can, doesn't mean you should. Unless you are hacking the linux kernel, use a type safe language folks.

</comment><comment><USER45> C macro magic? Run.</comment>


Marc Andreessen on Big Breakthrough Ideas and Courageous Entrepreneurs - <AUTHOR> http://www.datafox.co/blog/marc-andreessen-on-big-breakthrough-ideas-and-courageous-entrepreneurs/

<comment><AUTHOR> I took some notes from Marc's talk at Stanford last month... Video is 55 minutes, and it's packed with insights. Figured a blog post is a good way of capturing it - and with pictures! -Niv

</comment><comment><USER2> Nice.</comment>


Is Python call-by-value or call-by-reference? Neither. - <AUTHOR> http://www.jeffknupp.com/blog/2012/11/13/is-python-callbyvalue-or-callbyreference-neither/

<comment><USER2> Wrong. It's call-by-value, the confusion comes from what, in Python, is considered a value (and when a value is mutable, which is always (with a little ctypes magic), even the built-in number 4). And just like in Java, which also is always call-by-value, Python passes pointers-by-value under the hood which allows for some easy modification.

There is an easy test to see if a language supports call-by-reference or not:

        a = 3
        b = 4
        swap(a,b)
        print a, b # 4 3

C++ can do this, C can't, C# can't (if you think it can, then so can C), Python can't, Java can't. Therefore, all of those but C++ are call-by-value in all cases.

Edit: I hasten to add, the swap() function should be implemented "traditionally". i.e.:

        def swap(x, y):
          temp = x
          x = y
          y = temp

<comment><AUTHOR> To say that because Python passes pointers-by-value under the hood (which is true for cpython, but not other implementations) is not useful. "Call by value" and "Call by reference" have reasonably precise definitions, and Python fits neither. See <http://en.wikipedia.org/wiki/Call_by_sharing#Call_by_sharing> for a discussion of the difference.

<comment><USER3> Re: call-by-sharing: "Although this term has widespread usage in the Python community, identical semantics in other languages such as Java and Visual Basic are often described as call-by-value, where the value is implied to be a reference to the object."

It's behaviorally call-by-value, where the value is always an object reference.

The difference is only mildly interesting, and the fervor with which you're arguing about it is wholly uninteresting, bordering on misleading. This is a semantic distinction with little to no use, and just serves to further confuse the rather simple and clear behavior.

<comment><USER4> afaik, this approach is from (or at least strongly championed at) comp.lang.python, and i would guess that their experience is that it is the most successful way of explaining python to new users there.

it's not so terrible - it works well enough for c.l.p, and anyone used to call by value with references/pointers is capable of working things out themselves.

really, it's largely a python (and c.l.p) cultural thing. you're berating some guy who's just a messenger for something that, in its context, doesn't do any harm (indeed, might help some newbies) (i don't have any skin in this game, but the guys on c.l.p do great work helping others; if this helps them it's fine by me).

<comment><USER3> You're right, it doesn't do any harm. But I think the way he's presenting it is harmful, as it's more confusing and not intended as explanation, but more as pointing out a distinction that isn't really present. He's very fervently defending a minor point of semantics, and I feel that's harmful.

</comment></comment></comment></comment></comment><comment><USER5> Perhaps this is a digression, but there are some misunderstandings in this blog post about the memory model in his C++ example. Since the author goes to lengths to be precise and exact in terminology and his explanations, it would be good to clarify some things.

For the example code C++ given:

        string some_guy = "Fred";
        // ...
        some_guy = "George";

Here is the author's explanation:

        In the above, the variable some_guy refers to a location in
        memory, and the value 'Fred' is inserted in that location (indeed,
        we can take the address of some_guy to determine the portion of
        memory to which it refers). Later, the contents of the memory
        location referred to by some_guy are changed to 'George'. The
        previous value no longer exists; it was overwritten. This likely
        matches your intuitive understanding (even if you don't program in
        C++).

There are several incorrect statements here.

A. "the variable some_guy refers to a location in memory, and the value 'Fred' is inserted in that location" Not true. The variable some_guy refers to a location in memory, but that location contains a _pointer_ to a region of memory with the value "Fred". More on this in a bit.

B. "indeed, we can take the address of some_guy to determine the portion of memory to which it refers". Again, not true in the way the author probably intended. The address of 'some_guy' will be the address on the stack where the variable is located. The value at that address on the stack will be a pointer to a separate region of memory containing "Fred".

C. "Later, the contents of the memory location referred to by some_guy are changed to 'George'." To be precise, the contents of the memory location referred to by some_guy are changed to a new _pointer_ to a different region of memory containing "George".

D. "the previous value no longer exists; it was overwritten." The values "Fred" and "George" exist through the entire lifetime of the program. They are string constants, which are compiled in to the data section of the program binary. The author is correct on one point: the value of 'some_guy' is overwritten during the reassignment. However, the value in this case is simply a pointer, not a string.

We can verify my claims experimentally. On my system (Ubuntu 11.04 32-bit, g++ 4.5.2), I wrote the following test program:

        //a.cpp
        #include <string>
        using namespace std;
        int main()
        {
          string some_guy = "Fred";
          some_guy = "George";
          return 0;
        }

Compile like this: $ g++ -O0 a.cpp -o a

Now, we can disassemble the "main" function to see exactly what is going on in the executable:

       $ objdump -d a
       a:     file format elf32-i386
       ...
       08048614 <main>:
        8048614:	55                   	push   %ebp
        8048615:	89 e5                	mov    %esp,%ebp
        8048617:	83 e4 f0             	and    $0xfffffff0,%esp
        804861a:	53                   	push   %ebx
        804861b:	83 ec 2c             	sub    $0x2c,%esp
        804861e:	8d 44 24 1f          	lea    0x1f(%esp),%eax
        8048622:	89 04 24             	mov    %eax,(%esp)
        8048625:	e8 06 ff ff ff       	call   8048530 <_ZNSaIcEC1Ev@plt>
        804862a:	8d 44 24 1f          	lea    0x1f(%esp),%eax
        804862e:	89 44 24 08          	mov    %eax,0x8(%esp)
        8048632:	c7 44 24 04 80 87 04 	movl   $0x8048780,0x4(%esp)
        8048639:	08
        804863a:	8d 44 24 18          	lea    0x18(%esp),%eax
        804863e:	89 04 24             	mov    %eax,(%esp)
        8048641:	e8 ca fe ff ff       	call   8048510 <_ZNSsC1EPKcRKSaIcE@plt>
        8048646:	8d 44 24 1f          	lea    0x1f(%esp),%eax
        804864a:	89 04 24             	mov    %eax,(%esp)
        804864d:	e8 ce fe ff ff       	call   8048520 <_ZNSaIcED1Ev@plt>
        8048652:	c7 44 24 04 85 87 04 	movl   $0x8048785,0x4(%esp)
        ...

In this case, we are interested in the following two lines:

        8048632:	c7 44 24 04 80 87 04 	movl   $0x8048780,0x4(%esp)

and

        8048652:	c7 44 24 04 85 87 04 	movl   $0x8048785,0x4(%esp)

0x4(%esp) is the address on the stack of our 'some_guy' variable. We are moving an "immediate" (constant) value into that location. What does that immediate refer to? Again, let's find out experimentally:

        $ objdump -s -j .rodata a
        a:     file format elf32-i386
        Contents of section .rodata:
         8048778 03000000 01000200 46726564 0047656f  ........Fred.Geo
         8048788 72676500 00000000                    rge.....

Notice that the address of "Fred" is 0x8048778 + 8 = 0x8048780, which is exactly the immediate value from the disassembled main function.

This means that our variable "some_guy" really is a pointer, and nothing more. The strings "Fred" and "George" are statically allocated in the data section of the binary, meaning they stick around indefinitely.

</comment><comment><USER6> A lot of comments here are talking about how it's "really" just call-by-value. I want to explain why this is unproductive.

First, let's acknowledge that the context we're discussing is explaining to someone new to Python how Python works with function arguments.

Now, if someone asks if lead-acid batteries work using chemistry, and you say "they use physics", then they will probably end up with a fairly inaccurate picture of how lead-acid batteries work. What you've told them is _technically_ correct, but it isn't practically useful.

It is the same if someone asks how Python passes arguments to functions and you say "it's pass-by-value". You've told them something technically correct, but they're going to think inaccurate things like that passing an object to a function copies all of the object's properties like a struct in C.

Even if you say "it's pass-by-value but the values are not what you think", you haven't actually _added_ to their understanding by that phrase. You're better off explaining _conceptually_ how it works, describing it as "call-by- sharing", and then explaining what that means. It will signal their brain to allocate space for a new concept, and not to drag in erroneous ideas from existing concepts. After they understand how it works, you can explain that call-by-sharing is actually just a spoonful of sugar to help the call-by-value go down.

Here's the thing: words are there to help us communicate. If you use a word in a way that is technically correct but _conveys the wrong idea_ to your listener, you're doing it wrong.

Here's the other thing: this is computer science. In computer science, as in all math, you often encounter a concept that is "really just" some other concept. When you come to that realization about a concept that you understand superficially, it can be transformative, taking you to a deeper level of comprehension. When someone asks you about the concept, you'll want to share that deep comprehension with them, so it's natural to want to jump right into it and explain it by connecting it to the general case. But they _will not_ understand if you do that.

You can't teach someone addition by starting with ring theory, after all.

<comment> void-star On the contrary, I think inventing a new class of of "call-by-X" function arguments makes it harder to explain Python to newcomers. I would clarify that I think I'm talking about seasoned programmers, and you probably mean somebody who's a fairly new to programming in general. It's a tough balance to strike, but I still think common semantics are best.

The concepts of call-by-value vs call-by-reference are simple and easy to distinguish once somebody has understood this basic concept in languages. I don't mean to disregard newcomers, though. And in-fact I think that it is doing a disservice to them to make python sound like it is radically different and more complicated than other languages. Instead I think a newcomer is best served by helping them grasp basic, common language concepts as they apply to python as well as other languages.

I really also need call out that there seem to be a lot of inaccuracies with regards to other languages than python on this thread and even some confusing terminology and analogies by the article's author. Specifically there seem to be a lot of misunderstandings about C (and even C++) flying around and being perpetuated here.

For example, you compared passing a python object to passing a struct in C. Passing a C struct to a function does _not_ copy all the fields of the struct. It passes a reference (pointer) to the function, which the function then uses to access the fields (or properties if you will) in the struct directly at their original location in memory.

You do get automatic copies of primitive numeric types when you pass them to functions in C. The reasoning and distinction is pretty simple: they fit in hardware registers.

But, a struct, an array, or a string (or character array) is always going to be passed as a pointer. When you pass it to a function, the only way you get a copy of it is if you explicitly copy the value to a new location in memory and point to it. You can of-course also pass a pointer to a numeric value to a function as well (as in func(int *var)) instead of the actual value (as in func(int var)), in which case the same semantics apply.

<comment><USER6> >On the contrary, I think inventing a new class of of "call-by-X" function arguments makes it harder to explain Python to newcomers.

Then I think you may have forgotten what it's like to move to a call-by- sharing language for the first time.

>Passing a C struct to a function does _not_ copy all the fields of the struct. It passes a reference (pointer) to the function, which the function then uses to access the fields (or properties if you will) in the struct directly at their original location in memory.

I'm sorry, but everything you said there is wrong. Try it out for yourself: <https://gist.github.com/4073329>

<comment> void-star Wow ok.. I'm going to try to be polite. I don't think _you_ tried it as you suggest I do:

first shot: ~ gcc -o foo -ggdb foo.c foo.c: In function 'main': foo.c:31: error: 'flippedPoint' undeclared (first use in this function) foo.c:31: error: (Each undeclared identifier is reported only once foo.c:31: error: for each function it appears in.)

Typo fixed: s/flippedPoint/secondPoint

what's the output?

        "{1, 2}{-1, -2}"

EDIT: Yours is actually an interesting example, and you're right, i responded to quick from the typo fix and didn't actually grok it.

But, actually we're both wrong... and right? The real answer turns out to be a bit more complicated. In your example the copies are made (automatically) in the caller (main) which actually passes a pointer to flipPoint. Try disassembling your executable. You'll see what I mean.

<comment><USER6> [Double posting because I was out of my edit window by the time I saw your edit]

>But, actually we're both wrong... and right? The real answer turns out to be a bit more complicated. In your example the copies are made (automatically) in the caller (main) which actually passes a pointer to flipPoint. Try disassembling your executable. You'll see what I mean.

I'm really not sure that what the assembly code does is relevant. First off, once it's in assembly, the concepts of "caller" and "function" are gone completely. There are structures that correspond to what compilers typically emit when you define and call functions, but there is no "copy in the caller and pass a pointer to the function". There's just "copy this data on the stack, put the location into a register, and then jump to this location".

Furthermore, this is looking at implementation details, and what we're concerned here with are the _abstract concepts_ of how these languages work. If you look at the assembly, you're seeing how your compiler decided to handle it. If you have optimization on, flipPoint is going to be inlined anyway. The spec doesn't really care how the compiler achieves the result as long as it does achieve the correct result. One compiler might have it copy before jumping and pass a pointer, but another compiler could just as easily pass a pointer before jumping and then have the copy made after the jump. You would not know the difference.

But the way C _acts_ is as if the fields of the struct were being copied when they were passed to the function.

</comment></comment></comment></comment></comment><comment><USER7> How is this different from call-by-value, where all values are object references?

<comment><USER8> It's not, it's call-by-value-but-some-people-can't-accept-it

</comment></comment><comment><USER9> Isn't this by definition call by value? The reference / binding is passed by value. Changing the reference to point to something else will not make the other reference to point to the same too.

<comment><AUTHOR> Call by value would mean that a copy of the value of the argument is passed to the function without involving the original value at all. To say the binding is passed by value is not useful, as all languages would then be considered "call by value", since _something_ needs to be passed into the function, and we can just call it a value.

<comment><USER10> This argument is as old as call-by-... definitions, but... What python holds in the variables are references to objects. Those are the values you're passing, so it's call by value using this reasoning.

Some languages do actually hold the value itself (struct for example) in the variable, so there you get a copy of the value (the struct contents). In python you get the copy of a reference (which again, is the value held in a variable).

</comment></comment></comment><comment><USER11> I'm a C# developer who also programmed in Java in the past. C# and Java both pass pointers by value (which is what Python does too). This is a very important thing to learn when programming C# or Java. A method (or function) in any of these languages can either manipulate the object that the reference points to, or they can reassign the reference to a new object internally (which will leave the original object alone).

</comment><comment><USER12> this is over complicating the model to avoid saying what for some reason people are terrified to say: that objects are just pointers. when you pass an object you are passing the value of a reference. it's not that scary

<comment><AUTHOR> That's confusing the language specification with one of its implementations. In Jython, for example, objects are not "just pointers".

<comment><USER12> no, they are still just pointers. they might be pointers to something fancier than a struct of its instance variables and abstracted by java's object system but in the end it's still a pointer

</comment></comment></comment><comment><USER13> I have a C based mental model of what is happening. Can anyone comment on whether it is correct?

In C terms, I think of all objects (including primitives such as numbers) as a struct with a void* pointer that holds the actual data, and another field that describes how to interpret the data: struct Obj { void* data; ObjDesc descr; }

Whenever you do objA=objB you create an new struct Obj with objA.data == objB.data. The same things happens when you pass an object to a function: you create a copy of the Obj.

When you modify an Obj, two things can happen: if the Obj is immutable you change where void* data points to: n1 = 4 n2 = b1 n1 = 5 ==> n1.data != n2.data

On the other hand, if Obj is mutable, than you don't change void* data, you modify the memory pointed to by data: n1=[] n2=n1 n1.append(1) ==> n1.data == n2.data

Is this correct or is mental model going to bite me sometime?

<comment> void-star This is really far off, actually.

I don't mean to criticize. I think the best advice is to read K&R C again. And, as some other posters have suggested, take a look and at least a passing understanding of the memory model and assembly code generated by C/C++ compilers.

You'll see, for instance, that type information is not stored as a field in a C struct at all, it's purely a distinction for the compiler to use. A C struct, indeed any type of data in C, is _just_data_ in memory. At the machine level, there's no such thing as a type. Just registers, memory, and instructions. The compiler uses type information to generate the right instructions to handle the type.

And to clarify, type metadata _as_actual_data_ can exist in higher level languages, but it's definitely not a C thing.

<comment><USER13> I was speaking of a model of how python does things, based on a C style implementation. Not an explanation of how C works.

</comment></comment></comment><comment><USER14> Python is a truly bizarre language in this regard. It's behavior is unusual and similar only to a few other less well known niche languages such as C# and Java.

<comment><USER15> I genuinely can't tell if you're trolling by calling Java a "less well known niche language".

As for Python being bizarre, it's just call-by-value, as has been pointed out elsewhere. The only arguably bizarre part is that all values must be pointers to objects. But I target like that consistency. I prefer it to Java, where I always have to think about whether a variable is an object pointer or a primitive type.

<comment><USER3> As far as I can tell, he was being entirely sarcastic.

</comment></comment></comment><comment><USER16> Python is strictly a call-by-ref language. The author of the blog and many commenter here seem confused. There are two causes:

1\. Some objects are immutable.

This makes some calls seem to be call-by-value, but they are not. A simple look at the generated code makes this obvious. Integers, strings, tuples, ... are passed by reference. It's just that you can't mutate the object being passed.

2\. The behaviour of the assignment operator is different from most non- functional language.

In Python, assignment never mutate values. It assign a new reference to the 'variable' (i.e. the scoped name). This explains clearly why in functions, you can't mutate an immutable object even though it is passed by reference: assignment merely rebind the argument name to a new value.

That is why when you want to mutate something in a function in Python, your only recourse is to call a function on that object:

      i = 4
      s = 'George'
      l = [ 'me', 'you']
      foo (i, s, l)

      def foo(a, b, c):
        a = 3
        b = 'Mark'
        c.append( 42 )
        c = [ 2, 1 ]

      print(i, s, l)
      # prints 4, 'George', ['me', 'you', 42]

<comment><USER6> While Python works by passing object references, it is not call-by-reference as that term is usually understood. Call-by-reference means that assignment within a function will be seen in the calling scope. That does not happen in Python.

</comment></comment><comment><USER17> Well, regardless of the call-by-x discussion, this is actually a really good explanation of scope and state in Python. Code like:

      def grow(array):
        array.append(1)

      array = []
      grow(array)

leads to such needless complexity.

</comment><comment><USER18> I highly recommend this site: <http://pythontutor.com/> It'll parse and execute python code, including showing what variables are pointing to what obects.

</comment><comment><USER6> Since you explained name binding anyway, a useful way to describe Python might just be "call-by-binding".

</comment><comment><USER19> I'm not too familiar with Python, but the semantics of Python described here match every other language that I can think of (including C). Can anyone give me an example of a language that is actually call-by-reference?

<comment><USER2> C++ supports call-by-reference (and also call-by-value), here's an example of call-by-reference:

        #include <iostream>
        void swap(int &x, int &y) {
          int temp = x;
          x = y;
          y = temp;
        }
        int main() {
          int a = 3;
          int b = 4;
          std::cout << a; // 3
          std::cout << b; // 4
          swap(a, b);
          std::cout << a; // 4
          std::cout << b; // 3
        }</comment></comment>


Developer Lorem Ipsum - <AUTHOR> http://developerloremipsum.com/

<comment><USER2> I still like SCIgen.

<http://pdos.csail.mit.edu/scigen/>

</comment><comment><USER3> I thought developer ipsum would be text and 'random code' in various languages

<comment><USER4> Yep, I was expecting something like:

class Lorem(val ipsum: Dolor) extends Sit {

      def amet(consectetur: Adipisicing) : Elit = sed.do((eiusmod: tempor) => incididunt)

    }

</comment></comment><comment><USER5> I've heard a manager use one of the sentences here :-(

Small technical question, actually. How is this generated? I'm assuming Markov chains, but if you wrote it, more info would be appreciated!

P.S. Everyone who didn't do this already - refresh the page.

<comment><USER6> Looks more like a simple grammar to me. Fun.

<comment><USER7> [Determiner] [Noun] [Verb] [Preposition] [Determiner] [Noun].

"Simple" might be giving it a little too much credit.

</comment></comment></comment><comment><USER8> I read it all.

If you start chanting it, it has a relaxing, self-hypnotic cadence to it, and afterwards you've found the solution to last week's bug.

Btw, do these statements have a hidden meaning: "Some HTML compiled up the SOAP." "A XP interpreted down the JavaScript."

<comment><USER8> And... I just refreshed the page.

</comment></comment><comment><USER9> Surely something more like:

       int lorem( char* ipsum, int  dolor )
       {
          float amet = 0.0;

          for( int i = 0;  i < eiusmod;  ++i )
          {
             int consectetur = adipisicing( amet );
             tempor[ i ] += elit;
          }

          return sed * eiusmod;
       }

(-- variable names in latin: there's something to win friends and influence people.)

</comment><comment><USER10> Isn't the whole point of Lorem Ipsum to move the reader's attention _away_ from the text? This text does precisely the opposite.

</comment><comment><USER11> Something similar here, but command-line only and you need to give it sample input first, so not as useful: <https://github.com/ppearson/paragen>

</comment><comment><USER12> Oh dreadful, it's like reading an Enterprise solution deployment whitepaper.

</comment><comment><USER13> Lol. Was surprised to see kanban sprinkled in random places. Nice touch.

</comment><comment><USER14> How about generating some powerpoint slides with this?

<comment><AUTHOR> When I first thought of the idea I was seriously going to have my CV auto generated and see if any recruiters contacted me.

</comment></comment><comment><USER15> Nearly all the sentences have "some" in them.

<comment><USER16> Yeah, some more words for that function would be nice.

Some suggestions: specific, designated, compiled, selected, verified, established, elected, exclusive, assorted

<comment><AUTHOR> Done! Thanks.

</comment></comment></comment><comment><USER17> That is awesome!!

</comment><comment><USER18> Am I the only person who expected something like that?

        #include <stdio.h>
        void main() {
            printf("Lorem ipsum dolor sit amet...");
        }

</comment><comment><USER19> Selected DSL rebased on verified evangelist. Established TFS time boxed up any SOLID. Any distributed interpreted until elected JSON. Verified debugger interpreted around verified evangelist. Designated SOLID debugged except a evangelist. A agile time boxed beside compiled XP. One distributed time boxed without verified distributed. Compiled SQL time boxed on established pomodoro. Selected DBA synergised after the iteration. Selected recursive time boxed before any SOAP. Designated DSL compiled below the XP.</comment>


Pi in the Sky, Artists Fill the SF Bay Area With w/ first 1000 Digits of Pi - <AUTHOR> http://laughingsquid.com/pi-in-the-sky-skywriters-fill-the-san-francisco-bay-area-skies-with-the-first-1000-digits-of-pi/

<comment><USER2> Looks like Pi is still ahead, in the Tau vs. Pi arms race.

<comment><USER3> I hate tau. An event like this should be viewed as a cool nerdy thing, a celebration of the culture and fun of mathematics. Thanks to tau, nerds start bickering over a senseless and useless distinction as to whether a factor of two should be present in a transcendental constant. Did we really need another pointless division and source of argument in our society?

</comment></comment><comment><USER4> The headline here at HN really needs to add the word "Sky" between "Area" and "With", put "Skywriters" in place of "Arists", or both.

</comment><comment><USER5> Interesting that it was a failed kickstarter but they did it anyway. [http://www.kickstarter.com/projects/ishky/pi-in-the- sky?ref=...](http://www.kickstarter.com/projects/ishky/pi-in-the-sky?ref=live)

</comment><comment><USER6> They really should have done this for Pi day. I may have missed it in the article, was there anything significant about their choosing yesterday for this event?

</comment><comment><USER7> I'm all for obsessing over computing & visualizing pi ... but ... wow. That's nuts.

</comment><comment><USER8> What would happen if you made Pi = 1, then adjust all other numbers accordingly?

<comment><USER9> μ

\--

To clarify, that's not a thing you can meaningfully do. It makes sense to define your units such that, e.g. c=1, because c has units (distance per time). You're really adjusting your units, not your numbers. But π is dimensionless. Diameter and circumference are both measures of distance, and they are never equal.

If you say "this number times one equals that number", and the numbers aren't the same, then when you say "one" you don't actually mean "one" the way anyone else speaks about it. (By convention: whenever you can do multiplication, if there's something called "one", then "one" is the multiplicative identity.)

There's nothing to stop you from, e.g. renaming all numbers x -> x/π. But you wouldn't gain anything; you'd just say things like "1/π squared is 1/π", and "1 squared is π", and "π times 1/π is π". It looks weird, but what's happening is perfictly ordinary, just with weird names.

<comment><USER10> One could sort of satisfy naturalethic's wish by choosing a numeral system with pi as basis. Then pi would have digit representation '1' in this system. And yes, mathematicians have studied transcendental bases for numeral systems (Knuth in particular, see the short paragraph on <http://mathworld.wolfram.com/Base.html> which explicitly mentions pi as basis).

Of course it's not a good idea to base your day-to-day numeral system on pi, but I know that at least for irrational bases there are actual applications.

<comment><USER11> "One could sort of satisfy naturalethic's wish by choosing a numeral system with pi as basis. Then pi would have digit representation '1' in this system."

I'm pretty sure pi comes out to 10 base pi, not 1 base pi.

<comment><USER12> Numeral systems are not restricted to the set (base N).

"a numeral system with pi as basis" could be (base pi) _pi, where pi =- > '1' and pi_pi -> '10' and unity -> '0.1'.

<comment><USER11> I'd refer you back to philh's answer. You can do that and it isn't "wrong", but it is surprisingly uninteresting, as compared to a discussion of irrational or negative or imaginary bases.

</comment></comment></comment></comment></comment></comment><comment><USER13> How did they do that?

<comment><USER14> The artist used a company called AirSign, who fly five aircraft in formation to write messages as a dot matrix: <http://www.airsign.com/skywriting.php> You can read more on the artist's website here: <http://ishky.com/pi/>

</comment></comment><comment><USER15> And people are worried about oil? Not in the Bay Area!</comment>


How Verizon lets its copper network decay to force phone customers onto fiber - <AUTHOR> http://arstechnica.com/information-technology/2014/08/why-verizon-is-trying-very-hard-to-force-fiber-on-its-customers/ <comment><USER3> I hate to say it but this article reeks of Neo-Luddism.

Fiber is the future and it it here to stay. In trying to hold on to the old tech, these Verizon customers are forcing Verizon to stagger their support between two technologies for much longer than they would otherwise - <USER2> to diminished customer service and perceived reliability for both.

<comment><USER4> Imagine this was an article about a web server provider... the customers want to stay with their current server with 99% uptime, the provider wants to switch them to a faster server with 90% uptime.

In a properly regulated market the customer gets to choose the option that suits them best, but as everyone knows the situation with US ISPs is bananas, far too much power in the hands of too few companies.

<comment><USER5> A better analogy would be: you shared a web server with 99 other users, splitting the cost 100 ways. Then the provider installed a new, faster server (with marginally smaller uptime -- easily worked around by buying a backup battery), to which all 99 of your fellow customers migrated. You refuse to migrate and want to stay on the old server.

In a free market, the provider would say something like "that's fine, we'll keep maintaining the old server as long as you're willing to pay the costs", where said costs are now 100x of what they previously were, since you're no longer sharing the upkeep. You could then decide yourself if this were really the best use of your resources.

In Verizon's case, though, prices on copper POTS service are regulated and can't be raised to reflect upkeep costs. So when most customers switch to fiber, the money coming in from the few remaining copper customers doesn't actually cover maintenance costs. That means maintenance of the copper network is being subsidized by FiOS customers. Whatever Verizon's other evils (and I agree with you that there are many), a government-mandated transfer of wealth from fiber adopters to copper-clinging Luddites is not good policy.

<comment><USER6> > Whatever Verizon's other evils (and I agree with you that there are many), a > government-mandated transfer of wealth from fiber adopters to copper- > clinging Luddites is not good policy.

Most of what people perceive as Verizon's evils are the result of government- mandated transfer of wealth.[1]

The way most markets work is that new technology is introduced in the premium segment, and trickles down to everyone else once the fixed costs are amortized. In almost every city, Verizon is legally prevented from doing this. If they want to wire up a city with FiOS, they can't start with the rich neighborhoods and expand to poorer neighborhoods as it makes sense to do so. They have to commit to wiring up the whole city in one go. Because that's an unattractive proposition in most places, they've halted new FiOS deployment.

The same thing is true of the existing copper network. Many of the places that are currently wired shouldn't be wired. In a free market, they wouldn't be wired. It makes no sense to wire them, for remoteness or density reasons. The reason they're wired is that the government taxed certain of Verizon's customers to subsidize the customers that it doesn't make sense to build out to.

[1] I'm not against such programs as a principle, but when you try to implement it "by the back door" instead of through direct subsidy, as the government has done in the telecom sector, you get exactly the sort of dysfunction that we see now.

<comment><USER7> You're absolutely right. Rural America and poor people shouldn't have access to the internet or phone service. It is a horrible tragedy that we have a universal service fund that helped to give everyone in the country a basic level of service.

<comment><USER8> If we want to give these people/regions services then we should be clear about what we are doing and explicitly subsidize them, and fund it by explicitly taxing others. Instead, these policies implicitly subsidize them by implicitly taxing others.

</comment></comment></comment></comment></comment></comment><comment><USER9> Near the bottom of page 1:

> A fiber line that carries POTS traffic is still subject to utility > regulations.

A person at Public Knowledge told me that VZ is supposed to offer a POTS (well, TDMA) fully-tariffed-and-regulated voice service over FIOS.

That doesn't appear on their web pages, so I called to see if I could order it. Nobody in their call centers knows of such a product. Then I used a web- chat, and, surprise, the existence of such a thing was flatly denied.

I'm in VZ FIOS territory, but I'm not interested in being their customer.

<comment><USER10> [http://www.verizon.com/home/phone/#callingplans](http://www.verizon.com/home/phone/#callingplans)

They don't call it Fios, but it'll work over their fiber if you don't have copper connection.

<comment><USER9> No. If you live in a FIOS-served neighborhood and do not have existing copper, this product is unavailable. Instead you'll be offered an unregulated FIOS voice product.

</comment></comment></comment><comment><USER11> This is a problem I'd love to have.

It makes me sad that the FCC may put the brakes on fiber rollout because a few people are upset that their phones won't work during long blackouts. It must be a small population; I can't even name a single person under 50 that has a landline.

At some point telcos stopped supporting the telegraph too.

<comment><USER12> I'm closing in on 50 and I dropped my landline years ago. I had a VOIP service at home for a while because cellular signals is very poor at my house, but dropped that when I switched to Republic Wireless. Now my phone uses WiFi at home and Sprint's network when I'm mobile.

Yes, I'm now subject to failures of my home internet service (Comcast), my cable modem, and my wireless router, and if I lose power it all goes offline. But so far it hasn't been enough of a problem that I want to go back to the expense of a separate POTS service.

<comment><USER13> Note that what you think of as a landline and what is counted (for regulation purposes) as a landline are two entirely different things. It's entire possible that you didnt have a regulated landline service, even though it appeared to be one.

Verizon cares only about ensuring that their phone service doesn't fall under the regulations of phone service

</comment></comment></comment><comment><USER14> Perhaps I'm too sensitive but I feel the current Ars runs a lot of techno- sensationalist stories that often are inconsistent from one week to the next. Next week I expect to see Ars run a story chastising the US for its remaining high percentage of users still on slow copper internet whereas Europe and Korea have much faster internet speeds.

<comment><USER15> techno-sensationalism = page views = ad impressions = $$

</comment></comment><comment><USER13> It's not just Verizon - Comcast and AT&T are also trying to kill copper, because that would allow them the same unregulated control over phone service that they already enjoy over the Internet.

[http://www.timmins.net/2013/12/11/how-att-verizon-and- comcas...](http://www.timmins.net/2013/12/11/how-att-verizon-and-comcast-are- working-together-to-screw-you-by-discontinuing-landline-service/)

<comment><USER12> Of course, the natural response to regulation is to find a way around it. That's why so many of our regulations simply result in non-value-added overhead and higher costs for everyone.

</comment></comment><comment><USER4> Correct me if I'm wrong, but it looks like LLU (Local Loop Unbundling) is legally possible in the US: [http://en.m.wikipedia.org/wiki/Local- loop_unbundling](http://en.m.wikipedia.org/wiki/Local-loop_unbundling)

Every article I've read about Verizon suggests they're run by dickheads, I'm sure there would be plenty of people willing to switch to an alternative provider, why is LLU not popular in the US? To make it clear, LLU means you can lease the same copper or fibre lines to run your own ISP as long as you install your own routing gear in the exchange. I know it's very popular in the UK.

<comment><USER16> Under the current regulatory guidance from the FCC, LLU is only viable in the US for customers connected directly to a Central Office. For customers connected to a remote terminal, there is insufficient space to install 3rd party gear, and the incumbent carrier gets to be a monopoly on data services.

</comment></comment><comment><USER17> Not only is this incredibly sleazy, it's a sign of things to come; it's incredibly clear that Verizon has ceased to give anything reminiscent of a fuck about their networks. How do you think that bodes for the longevity of LTE? Or FiOS for that matter?

Regardless of either, the PSTN is a great tool, and an incredible, self sustaining network. The fact that Verizon would shrug it off in favor of running off with the money Netflix gave them to resolve their peering dispute or what have you instead of trying to innovate illustrates their future in telecommunications.

Though I think by now, it's incredibly obvious it isn't a bright one for consumers. With any luck, a company like Frontier who gives something beyond a negative fuck in regards to the wireline network will end up buying it up and smashing Verizon with it.

<comment><USER10> Why should Verizon be forced to support an incredibly aging network that people are abandoning left and right?

50% of Americans don't use a landline.

Verizon offers phone service on fiber.

<comment><USER17> Source? As far as I was aware, it was around 40% that didn't, and the decline has slowed pretty drastically in recent years. Not to mention it's hardly a rarity in businesses.

In any event though, you're really missing the point here; the problem isn't abandoning copper. It's not providing an equivalent grade of service to one we've had for decades now.

Verizon has long supported regulated POTS service over FiOS. They're now trying to push people towards a form of phone service that's no longer regulated for quality or price, among other things such as the requirement to share the outside plant with competitive carriers. You know how the push for net neutrality is gearing towards Title II classification for internet? That's precisely what they're trying to steer clear of; they still want to offer phone service, but offer it as an unregulated Title I service - much like the internet is classified right now.

As for the issue of local instead of common battery, the POTS standard requires 50 milliamps of power. I'd be absolutely shocked if there was no way to reliably deliver that over fiber.

tl;dr, this is really more a regulatory issue then a technology one. It's actually very similar to the debate going on about the internet right now. Technology is just the excuse being given to getting around Verizon's obligations as a common carrier.

<comment><USER10> >[http://www.cdc.gov/nchs/data/nhis/earlyrelease/wireless20140...](http://www.cdc.gov/nchs/data/nhis/earlyrelease/wireless201407.pdf)

40% don't have access and a bunch more never use it. And that is including people who use VOIP and services like cable company voice services.

>In any event though, you're really missing the point here; the problem isn't abandoning copper. It's not providing an equivalent grade of service to one we've had for decades now.

We aren't owed a super reliable voice backup service. Especially if its not economical.

>You know how the push for net neutrality is gearing towards Title II classification for internet? That's precisely what they're trying to steer clear of; they still want to offer phone service, but offer it as an unregulated Title I service - much like the internet is classified right now.

Verizon is abandoning it and nobody is rushing in to take over. I wonder why? It's an unprofitable business. Are you going to invest your lifesaving's in a POTS service? Of course not.

>Technology is just the excuse being given to getting around Verizon's obligations as a common carrier.

Not get around it, Verizon is seeking to avoid being one. Unless you are willing to regulate the shit out of Apple's facetime, skype, google video, etc. etc, it should be no shock that Verizon wants out.

Buy an old flip phone if you want a back up for a major outage.

<comment><USER17> > 40% don't have access and a bunch more never use it. And that is including > people who use VOIP and services like cable company voice services.

Cable and voip likely have more to do with bundling, and Verizon actively trying to force people to FiOS/off their network then anything else. They clearly aren't competing to modernize their DSL services to compete against cable, and as such, people have switched over to their internet service. Take a look at any thread with 'Comcast' in the subject line. I don't think I have to tell you people feel stuck with their services. Anyway, since bundling has become such a common practice, it's likely much more economical to buy phone service from them instead.

> We aren't owed a super reliable voice backup service. Especially if its not > economical.

So let me get this straight - a service that the majority of the country has, and can be found in nearly every business across the country isn't economical? A grade of reliability we've had for decades isn't economical anymore? I suppose unlimited internet plans aren't either.

> Verizon is abandoning it and nobody is rushing in to take over. I wonder > why? It's an unprofitable business. Are you going to invest your > lifesaving's in a POTS service? Of course not.

Actually, Frontier, Fairpoint, Windstream and Hawaiian Telecom have bought out a ton of states Verizon used to provide service in, but okay. Verizon isn't selling the other states they still service; that's why we're hearing about them refusing to maintain their infrastructure. Not these other companies. Hell, from what I've been hearing, things have gotten better under their ownership.

> Not get around it, Verizon is seeking to avoid being one. Unless you are > willing to regulate the shit out of Apple's facetime, skype, google video, > etc. etc, it should be no shock that Verizon wants out.

Those are commodity services that operate over other peoples' infrastructure. Let me know the day when any of these services operate dedicated links between peoples' homes, and starts offering 911 services, faxing, circuit switched data transport, interactive voice response services, and the myriad of other things people use the phone network for.

</comment></comment></comment></comment></comment><comment><USER18> In Los Gatos [http://en.wikipedia.org/wiki/Area_codes_408_and_669#Los_Gato...](http://en.wikipedia.org/wiki/Area_codes_408_and_669#Los_Gatos), the copper network is all there is. I can get DSL with up to 3Mbps. I have a cell phone and cable modem.

</comment><comment><USER19> If VoIP actually used the capability to deliver higher call quality, that would be one thing. But these services almost universally sound like vocoder compressed noise in an echo tunnel. They're as bad as cell phones, and have as much latency, which makes conversations very hard. "Could you repeat that" and people talking over each other is much more common now than 20 years ago.

<comment><USER20> I'm struggling with a couple of VoIP services vs the occasional Skype call. As much as I dislike Skype, the call quality is _much_ higher, especially when I'm using HSPA or similar.

</comment></comment><comment><USER21> I'm under the impression that the extra redundancy that Bell and co have to maintain power on phone lines during an outage is precisely what makes it so expensive relative to VoIP products.

Would it not make more sense to demand the utilities provide more reliable power in the face of an emergency (even at increased cost)?

<comment><USER12> I think that depends on the increased cost. Do I want to pay more to avoid a couple of hours without power during a storm? proably not, it's not really a big deal. Do I want to avoid multi-day power outages? Sure, but those are rare and a system that is fully resistant to e.g. major hurricane damage might cost be more than people want to or can afford to pay for.

</comment></comment><comment><USER22> Fiber? More like to their capped and useless mobile networks. Verizon stopped expanding their fiber optic network long ago except in a few areas where they are bound by contracts, and even there they cheat and don't finish the work. I wish Verizon would offer fiber instead of copper lines. No dice.

</comment><comment><USER23> So they don't want to deal with the hassle of supporting two kinds of networks at once. Big deal. Either find an alternative, deal with their decision which is driven by other customers (not some sort of imaginary corporate sociopathy), or start your own. Whining helps no one.</comment>


A History of Capacity Challenges in Computer Science [pdf] - <AUTHOR> http://cs.stanford.edu/people/eroberts/CSCapacity.pdf <comment><AUTHOR> This has an interesting perspective on why the number of CS grads in the US seems so cyclical. The author argues they aren't, and the 1980s crash was a capacity crisis, totally different from the 2001 dot-com bubble crash. He also argues that we are about to experience another capacity crash, as there are not enough CS PhDs who want to go to teaching, and therefore enrollment will be capped.</comment>


Discrete Analysis – A new type of math journal - <AUTHOR> https://gowers.wordpress.com/2015/09/10/discrete-analysis-an-arxiv-overlay-journal/ <comment><USER2> Long-overdue and laudable. Interesting to see how it plays out and whether other fields can follow suit; I think my own would take to this idea fairly well.

Also interesting to see that they haven't decided to change much about the peer review process. The arXiv overlay structure prohibits a practise that I've been hoping to see introduced, which is blinding reviewers to the name/s and institution/s of the author/s.

<comment><USER3> Does "double blind" work in your field? It does not in mine (compilers, programming languages).

As an author, I can usually identify the anonymous reviewers as well, if they are not outsiders.

<comment><USER2> The vast majority of papers in quantum information and foundations involve proofs of some mathematical results and some discussion of their physical/operational relevance. I don't see anything to prevent it in principle. What stops it working in your field?

<comment><USER3> The community is small and everybody knows each other.

</comment></comment></comment></comment><comment><USER4> There are several arxiv overlay journals. Example: [http://www.emis.de/journals/SIGMA/about.html](http://www.emis.de/journals/SIGMA/about.html)</comment>


A Formula for Bayesian A/B Testing - <AUTHOR> http://www.evanmiller.org/bayesian-ab-testing.html <comment><USER2> Very nice, much faster than simulating it. I guess if you're using an informative prior, instead of adding 1 to the number of successes and failures, you add the corresponding parameters of your beta prior?

A pretty good shortcut (if you don't have a log-beta function, for example) is to approximate both A and B with the normal distribution. Then their difference is also normal, so you can just check the probability that a normal random variable is greater than zero.

Specifically, the mean μ of a beta distribution is α/(α+β) and the variance σ^2 is αβ/((α+β)^2(α+β+1)). Use these as the parameters for your normal approximation, and we have the difference D ~ N(μ_A-μ_B, σ_A^2+σ_B^2). The probability that B beats A is just the CDF of D evaluated at 0.

In Python:

      from scipy.stats import norm as norm

      def beta_mean(a, b):
          return a/(a+b)
      def beta_var(a, b):
          return a*b/((a+b)**2*(a+b+1))
      def probability_B_beats_A(α_A, β_A, α_B, β_B):
          mu = beta_mean(α_A, β_A) - beta_mean(α_B, β_B)
          sigma = (beta_var(α_A, β_A) + beta_var(α_B, β_B))**.5
          return norm.cdf(0, mu, sigma)

<comment><USER3> For those of us stuck in Java/Scala land, with attached results: [https://gist.github.com/krishnanraman/4be978cbe8979e4c97fa](https://gist.github.com/krishnanraman/4be978cbe8979e4c97fa)

</comment></comment><comment><USER4> I'm pretty sure this formula is correct, but I haven't seen it published anywhere. John Cook has some veiled references to a closed-form solution when one of the parameters is an integer:

[http://www.mdanderson.org/education-and- research/departments...](http://www.mdanderson.org/education-and- research/departments-programs-and-labs/departments-and-divisions/division-of- quantitative-sciences/research/biostats-utmdabtr-005-05.pdf) [pdf]

But he doesn't really say what that closed form is, so I think his version must have been pretty hairy. (My version requires all four parameters to be integers, so I doubt we were talking about the same thing.)

Sadly I couldn't get the math to work out for producing a confidence interval on |p_B - p_A| so for now you're stuck with Monte Carlo for confidence bounds.

Thanks to prodding from Steven Noble over at Stripe, I'll have another formula up soon for asking the same question using count data instead of binary data. Stay tuned!

<comment><USER5> Thanks for doing this, Evan! Much appreciated - I've already implemented it to compare with our traditional confidence score based A/B metrics.

I'm not sure what you mean by Monte Carlo for confidence bounds - is there some reading I can do to brush up on that?

edit: here is your solution implemented in Ruby as I implemented it [https://gist.github.com/nickelser/6dfa0f2737c502dae267](https://gist.github.com/nickelser/6dfa0f2737c502dae267)

<comment><USER6> Confidence interval means that is expected conversion rate of A is 0.5 and for B is 0.4, what is the actual spread. In other words, say, for 95% of the time does the conversion rate fall in the 0.3-0.5 range or 0.35-0.45 range. More data you collect , narrower this range will be. Confidence bounds for individual conversion rates can be calculated from the formula since they follow beta distribution. What Evan is saying that the confidence bound for the difference in conversion rates would need to be calculated through repeated simulations and taking (say) 5 percentile and 95 percentile of the values that come.

For example, given you know probabilities for A and B (say 0.5 and 0.4) and that number of trials has been 100, you can simply run thousands of simulations, know what the conversion rates come out to be in respective simulations and aggregate the difference in conversion rates at the end of each simulation. That would be a confidence bound.

<comment><USER5> Gotcha - thanks so much for writing this up. I'll see about actually implementing this :)

</comment></comment></comment></comment><comment><USER7> _If you are unable to find a log-gamma function lying around, rewrite the above equation in terms of factorials using Γ(z)=(z−1)!, and notice that there are an equal number of multiplicative terms in the numerator and denominator. If you alternately multiply and divide one term at a time, you should be able to arrive at an answer without encountering numerical overflow._

Something about the right side of the equation immediately preceding this quote seems to indicate that _many_ of the terms in the numerator would cancel with equivalents in the denominator. I'm not really familiar with CAS systems, but is this the sort of thing they could do? Doing this simplification _once_ when one writes the code seems to be a win over calculating the original expression every time the code runs.

<comment><USER8> I had the same thought, and went down the simplification rabbit-hole. Here's where I ended up: [https://gist.github.com/avibryant/545e63b446a840951d5e](https://gist.github.com/avibryant/545e63b446a840951d5e)

</comment></comment><comment><USER6> Evan, does this formula account for multiple comparisons (if you have multiple goals and multiple variations)? I guess it would suffer from the same problems that if you have 100 variations and 10 goals, some of them are bound to produce a significant result, just by random chance. Isn't it? In classical testing, you can fix it by making your calculated alpha smaller than the real alpha, so you need much more data if there are multiple comparisons. What happens in Bayesian case?

Edit: I did some Googling and found this [http://www.stat.columbia.edu/~gelman/research/unpublished/mu...](http://www.stat.columbia.edu/~gelman/research/unpublished/multiple2.pdf)

<comment><USER9> That paper is a very different setup --- the hierarchical model Gelman, Hill, and Yajima use implicitly builds multiple comparisons into their process. You could do the same thing here in principle by drawing alpha and beta for different experiments from an underlying distribution, but I'd be cautious about applying that approach mindlessly.

If you haven't read it, Gelman and Hill's book is excellent: [http://www.stat.columbia.edu/~gelman/arm/](http://www.stat.columbia.edu/~gelman/arm/) and so is Gelman's blog.

</comment></comment><comment><USER10> Great work! However, an additional _testing_ section would be nice (right below the _implementation_ section).

That section should provide two or three lists of example input values, and the expected output value (up to some accuracy).

As the author notes, although this formula looks pretty simple, you can make a lot of numerical mistakes when implementing it. A test suite would help implementors to catch those mistakes early.

<comment><USER11> Assuming the Julia implementation is correct, you could write tests based on output from that.

</comment></comment><comment> hidden-markov Indeed, much faster than Monte Carlo integration:

      5.778  evaluation.py:20(evaluate) # Sampling version
      0.043  evaluation.py:64(evaluate) # Closed formula

(See my A/B testing library [https://github.com/bogdan- kulynych/trials](https://github.com/bogdan-kulynych/trials))

</comment><comment><USER12> Here's the Mathematica version:

PrBbeatsA[\\[Alpha]a_, \\[Beta]a_, \\[Alpha]b_, \\[Beta]b_] := \\!\\( \ _UnderoverscriptBox[\\(\\[Sum]\\), \\(i = 0\\), \\(\\[Alpha]b\\)] \_ FractionBox[\\(Beta[\\[Alpha]a + i, \\[Beta]b + \\[Beta]a]\\), \\(\\((\\[Beta]b + i)\\) Beta[ 1 + i, \\[Beta]b] Beta[\\[Alpha]a, \\[Beta]a]\\)]\\)

</comment><comment><USER13> A minor complaint: that formula calculates a probability (a number within the range [0,1]), rather than odds (a ratio of expected values).</comment>


Nationwide Closure of BJ's Wholesale Caused by Software Failure - <AUTHOR> https://twitter.com/MarciRubinN12/status/1060573958196600833?s=20 <comment><AUTHOR> I would love to hear a post mortem on the bug that caused this outage.

It's slightly disturbing to me that a software issue can cascade enough to take down an entire retail chain. How many more systems are designed with such vulnerabilities?</comment>


Desktop Metal – Affordable metal 3D printing - <AUTHOR> https://www.desktopmetal.com/ <comment><USER2> This company's pages make a big deal about the 'secure cloud based software'. What they don't say is that the security is for them against Chinese clones. It is security against you, the owner, from owning your device.</comment>


The Corn Personality Test: are you modular or integrated? - <AUTHOR> http://bentilly.blogspot.com/2010/08//analysis-vs-algebra-predicts-eating.html <comment><USER2> That's an interesting theory and it fits nicely with the latest pop psychology fad, but all things being equal, I'm gonna have to go ahead and call bullshit on this one.

Anecdotal counter-evidence: I'm mostly a row-eater but I'm an analyst. And he goes on to state that algebraists prefer object-oriented programming whereas analysts like Lisp. Yet, I prefer non-dogmatic OO.

I don't recognize myself anywhere in this model. Do you?</comment>


Interview with Patrick Volkerding, Founder of Slackware Linux - <AUTHOR> http://www.linuxquestions.org/questions/interviews-28/interview-with-patrick-volkerding-of-slackware-949029/#post4697882

<comment><USER2> Great interview.

Patrick and the other Slackers always have done a great job with this distro, and I've been a satisfied user since the fall of '93. (Except for having to pull all those blasted floppies down over a 14.4kb v.32bis modem, and praying the line didn't drop.)

I've always told people that if you just want to run Linux, get Ubuntu; but if you really want to LEARN Linux, run Slackware.</comment>


Improved Productivity: A 12-Step Program - <AUTHOR> http://webworkerdaily.com/2010/02/23/improved-productivity-a-12-step-program/

<comment><USER2> A favorite productivity hack: if you've got a project that you know how to finish and estimate it'll take an hour or less, instead of finishing it at the end of the day, save it til morning so you can start off productive.

This fits well with their #2 and #9 (plan tomorrow, and start tomorrow with your plan), and as a bonus, helps keep you from getting stuck on e-mail or social networking sites (#7,8).</comment>


About CoverSutra, the Mac App Store and Sanity - <AUTHOR> http://www.sophiestication.com/blog/about-coversutra-the-mac-app-store-and-sanity/

<comment><USER2> Yuck. For me her explanation just makes the situation worse: 'I forgot what I'd promised, but now that I've been reminded of it, buck up and just spend the $5 to get what I'd told you would be included in your original purchase. I'm not trying to screw anybody over and I'm offended you said I am.' Really?! Shockingly not customer-centric.

Not that this excuses the rude anonymous idiocy against her on Twitter and her blog comments - I never understand why people yell and say ugly things even before asking nicely. Really unpleasant and wrong. But her behavior certainly doesn't make me want to purchase anything she makes in the future, either.

<comment><USER3> Her response was written entirely from her perspective. Shockingly bad customer service, indeed.

</comment></comment><comment><USER4> I have no idea why the dev is choosing to take a major hit in her reputation that she clearly worked pretty hard to build through great design and code, for no reason other than "I don't want to spin up a separate build".

And for what? If you're gonna burn through your company's rep, at least get something out of it. Contrast this to what another Indie Mac dev did when confronted with the same situation:

"Free updates to the 1.x product will continue for paid customers of TapeDeck. It means a bit of extra maintenance on my part, but I will do my best to keep my existing customers happy."

<comment><USER5> Why doesn't she simply offer App Store coupon codes to existing users? Seems like that would solve her "PR" problems, and they expire after their first use so there shouldn't be any worry of piracy or anything like that, as with serial numbers.

<comment><USER6> As far as I know, you only get 50 of those per release.

<comment><USER7> Per version, I believe. You have to game the system a little bit for that, but that's what Marco does according to the Build & Analyze podcast, from what I recall.

</comment></comment></comment></comment><comment><USER8> Crazy- she could have scored points out of this! Price at $0.99 out of the gate, email the ENTIRE userbase explaining the situation. Say that there is no way to migrate existing users to the App Store, but in appreciation of their support she's pricing at 99 cents for the first week so they can upgrade.

Explicitly state that 'Even though I'll lose a bunch of money by pricing so low during the launch of the App Store, it's worth it to me to maintain the trust of my users'.

Boom. Crisis averted, you lose $5-10k or so on launch sales, but make very, very loyal users out of your existing customers. Cross-sell that email list more aggressively in the future. Money in the bank.

</comment><comment><USER9> > If I thought of that license text I would have simply declared this new > version 3.0.

Ouch. Despite what I think she's trying to claim, this statement shows that she clearly had no intention of keeping the "free upgrades til 3.0" promise.

Kind of a bum deal for her customers.

<comment><USER10> The tragedy is that it could all have been averted by numbering the 2.5 release as 3.0. From the comments on her original blog post my impression is that most complainers don't seem to understand that version numbers are arbitrarily defined by the developer.

It's not hard to imagine she might have gotten good PR by announcing that v3 is out and, per the promise, a simple paid purchase through the app store.

</comment></comment><comment><USER11> She has certainly lost my trust. She made a promise to help her business -- possibly impacting competing products like BowTie who did not make the same promise negatively -- and then fails to uphold it. Not only is this a breach of contract with her customers, it undermines our ability to find viable alternative products.

A promise doesn't mean "I'll do it if it's convenient." That people are rewarding her broken promise is just bizarre.

</comment><comment><USER12> I understand the difficulties of running a small shop, but the right PR move here is to suck it up, apologize to customers, and do what you can to make it right. If that means having to take some time out to spin new builds until you hit version 3.0 then so be it.

<comment><USER7> Sounds like the only reason to stick to her position is principle; it's not like she isn't going to take a financial hit from this.

</comment></comment><comment><USER13> The way this was handled certainly has an "ick" factor, but I wonder if it will matter from a pure business perspective in the end. Hell. It may even help. It's got a lot of people talking about her...

Here are some other thoughts/questions:

1) Users have been receiving free upgrades for years, meaning she received no additional revenues from them. I wonder many of the vocal unhappy users would upgrade to the app store version anyway if she just called this 3.0. How "valuable" is the livid segment of her existing customerbase?

2) The app store will allow her to reach new users that don't know or care about the developers. The Mac App Store is a black box where the developer reputation doesn't seem to matter much, unless its reflected in the reviews. Think about buying an iPhone app... Have you ever researched the reputation of a dev before buying a $4.99 iPhone app? My brother would just buy the app and be happy as a clam.

3) She has 70 reviews in the store so far. Only 1 mentions this and gives her 1-star. Many give 5 stars and some talk about how they are happy to pay for this upgrade.

Again, not condoning the behavior... Just thinking out loud.

</comment><comment><USER14> I think it's pathetic to whinge about having to drop just $5 on an app store version when a lot of users originally got it through a discount bundle such as Macheist. Especially as it will update to any future version (3.0+) for free and the existing app isn't going to expire, as stated in the blog post.

Look at any other company, Apple isn't going to give you a free code to say, Aperture 3 just because you own the pre-MAS serialized version.

<comment><USER6> People aren't mad because it costs $5, they're mad because she broke a promise.

And if Apple had reneged on a promise that all Aperture 2 users would get Aperture 3 for free, you better believe it would get huge (negative) media coverage.

</comment></comment><comment><USER15> maybe it's because I'm getting old and crotchety, or perhaps it's because I didn't get a good night sleep last night (it's stinking hot here and we've no air-con), but my internet today is full of self-entitled whiners.

ffs people, cut the lady some slack. all this fuss over a $5 cover art app!?

Sure, she broke a promise, there doesn't seem much doubt about that, but don't we all? Who here has never broken a $5 promise? Who's never said they'd be home at 6, but didn't get in 'til 6:30? Who's never said they'd meet for coffee, but then couldn't make it?

And no, I don't accept the argument that this is the same as if Apple reneged on an Aperture promise or similar. It's not binary, it's a scale, but $5 is at one end and $100 is a lot further up.

I realise this is an unpopular position, but I just can't stand all this negative attitude all heaped on one person. would you behave like this if she was in the same room as you?</comment>


Leftism Fails at Basic Economic Incentives - <AUTHOR> https://medium.com/@unlocking/leftism-fails-at-basic-economic-incentives-4dbd1c9f133e <comment><AUTHOR> Follow up article: Misunderstanding Economics — The "All Or Nothing" Fallacy. [https://medium.com/@unlocking/misunderstanding-economics- the...](https://medium.com/@unlocking/misunderstanding-economics-the-all-or- nothing-fallacy-c50b236ea33b)

</comment><comment><USER2> Coefficient of elasticity. It's fundamental to understanding how incentives work. It's also something you learn in Econ 101 or from any basic economic textbook. This guy clearly hasn't bothered to do his homework.

<comment><AUTHOR> (% change var A)/(% change var B). Are you saying this proves or disproves something? Please elaborate.</comment></comment>