Simon blogs

Started by Simon, October 18, 2015, 06:05:44 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

ccexplore

Quote from: Simon on November 30, 2017, 07:16:26 AMGoogle search offers several tabs: [all] [images] [videos] [shopping] ...
But sometimes, it switches around: [all] [shopping] [images] [videos] ...

Because the first sequence is so common, lazy Simon is conditioned that Google's image search is on the second button from the left, and always clicks that second button without reading. Occasionally, that lands me on the shopping page. A nasty way to peddle stuff. :lix-glare:

Hmm, I guess it could also be A/B testing (Amazon e.g. does tons of these on its websites for obvious reasons), though with the first sequence being so common already it doesn't seem like ideal conditions for setting up such an experiment. 

Also, any chance it's just a bug that repros on certain specific conditions?

All that said, I do like the idea that it could be an intentional clickbaiting technique.  If that's the case then I expect the "video" position to be even more prone to the switcheroo, under the assumption that video searches may be more popular (granted, I have no data to back that up).

To be somewhat fair, I suspect Google has monetization affordances built into all 3 places anyway.  Shopping may be most obvious and direct, but then considering that the other areas may enjoy greater volume of views and clickthroughs, which actually winds up generating the most revenue may well defy expectations.

Simon

Quote from: ccexplore on November 30, 2017, 09:10:16 AM
Also, any chance it's just a bug that repros on certain specific conditions?
All that said, I do like the idea that it could be an intentional clickbaiting technique.

I tested some more, and it seems to depend on the search term. The first slot is always [all], but the second slot depends on what Google thinks is most helpful.

If I search for "concrete" or "brick pillar", I get [images] in the 2nd slot. "music" gives [videos] in the 2nd slot, makes a lot of sense. "linux", "microsoft", "apple", I get [news] in the 2nd slot.

I guess that I never use Google for anything than the [all] and [images] tabs, and therefore only want to switch tabs when I want to image-search. But when I image-search, my search term is designed to give specific results on the [images] tab. Apparently, Google agrees that my image-searching terms are very often good image-searching terms. Only sometimes, it disagrees, and that leads to the switcheroo of the 1-2 posts above.

Undo

Quick-and-dirty undo by savestates is not that bad. It's fine if undo is moderately expensive.

I haven't written undo yet, even though the D Lix editor is approaching two years of age. I want the noble Command OO pattern, but that needs changes in the editor code everywhere. The result is that Lix has not even the quick-and-dirty undo.

I should advertize the issue tracker more and have others sort the issues by urgency.

-- Simon

Colorful Arty

Which is better through: code that may take a lot of resources (not very spacially efficient) that is fast, or slower code that makes the code as a whole look sloppier? It's one of those compromises a programmer must take.

nin10doadict

I'd like a catchy sing along that teaches people proper garbage etiquette, such as how to use a compactor properly. We can't have everything we want, though...

Please don't throw chairs in the compactor, guys.

Simon

Quote from: Colorful Arty on December 01, 2017, 02:02:30 PM
(not very spacially efficient) that is fast,
or slower code

The fast code, almost always.

On modern machines, CPU power is cheap, RAM is plentiful, but RAM access is slow. When the code is fast, you're often efficent enough in space already.

I bought 2 GB of RAM when the old laptop's 1 GB was not enough to build Lix anymore in late 2015. The reference D compiler never frees any memory until the entire project is built. That's fine, quick build times are paramount.

The tumbler activity is slow in Lix because it checks so many pixels during flight, hitting a ton of RAM. I have a rewrite on the backburner that is much faster, but doesn't preserve the exact physics. Hard decision whether/when to merge that.

I've never taken computer science classes, the typical examples there are probably merge sort versus quicksort? Merge sort is always N log(N), quicksort can be N^2 in worst case. But quicksort is often N log(N) on average and doens't have to allocate heap memory (the recursion goes on the stack, which is fast), therefore it's very good already.

-- Simon

Nepster

Quote from: Simon on December 02, 2017, 06:04:39 AM
I've never taken computer science classes, the typical examples there are probably merge sort versus quicksort? Merge sort is always N log(N), quicksort can be N^2 in worst case. But quicksort is often N log(N) on average and doens't have to allocate heap memory (the recursion goes on the stack, which is fast), therefore it's very good already.
Don't forget one more consideration: How does the algorithm perform in some of the most common cases in practical applications? In this example, this means to consider also the case: How does the algorithm perform for (almost) sorted lists?
For example a quicksort algorithm that always chooses the first (or last) element as a pivot element would be terrible, because that would mean a runtime of O(N^2) for already sorted lists, while ideally it would be close to O(N).

Simon

Thanks for mentioning the pivot element choice. Since I always call standard libraries and have never written quicksort, I didn't know how difficult pivot element choice could be. Nice evening with googling.

The only sort I've ever hand-written was selection sort. That seemed necessary when I wanted to print a list of L++ players ordered by score after a networking game, in 2009. Back then, I didn't feel that standard libraries should have something for such a problem. :lix-tongue:

Lemmings Heaven

This was a website around 2007 to 2013 at lemmingsheaven.info, but the domain is dead nowadays. They focussed on level design with vanilla Lemmini. Lacktardo and Dodo built many levels and released on Lemmingsheaven, but I haven't seen them anywhere. Pieuw and Prince Jamie were active, too, and have registered on Lemmingsforums since.

The Lemmingsheaven work is important history: Icho got into level design by finding youtube videos of Pimolems and Dovelems. He values these packs so much that he has converted them entirely to NL and maintains them to this day.

Lemmingsheaven had an IRC channel that is also dead today. I joined a few times around 2010, but I forgot any names of active IRCers there. I merely remember this conversation:

Simon: Have you looked at Lemmingsforums?
Person: No. They don't seem to focus on level design as much as Lemmingsheaven does. Lemmingsforums focuses far more on fan-art.


Well, this is certainly not true anymore. Today, Lemmingsforums focuses on software development. :lix-evil:

-- Simon

Simon

How to prevent bugs by design

When you write a Lemmings-like game and you need replay functionality, do not let assignment clicks issue both a replay action and affect the lemmings directly. Instead, issue only replay actions. Then, for the next physics update, fetch these new assignments from the replay and apply them. If you design like this, you will kill an entire class of replay-desyncing bugs outright. This is good design.

The replay collection on github is not only a check that I ship 100 % solvable levels. It has massive value as an integration test. When the replays pass and not fail, I know that the physics, the tile-loading code, the replay code, etc.,  all behave well together.

D has built-in coverage analysis via optional compiler switch, and I gave that a go: Lix's unittests and the replay verification together cover about 40 % of the entire codebase. Compared with professional software development, 40 % is lousy coverage, you would rather aim at 90 % to 100 %. But most of the uncovered code is editor, GUI, networking, and similar areas that were not written with unittesting in mind. Considering that I haven't written those areas with tests in mind at all, 40 % coverage is surprisingly high.

For the networking, I wrote a separate small command-line tool that connects to the server and can chat, which was useful during the network development. But this is not automated testing.

When you have a complex program that depends both on some abstract model and on outside dependencies like images or networking, don't let the model depend on the outside dependencies. Ideally, make the model self-contained, therefore easy to test, and merely feed the outside dependency into the model as a very last step. Example: In Lix, multiplayer replays can play from a replay file, you don't need networking to watch that. The replay can play back locally with all of the physics and different player colors. The networking would merely supply inputs for the physics during a match.

-- Simon

Forestidia86

I'm not sure if that's off to what you've said but there is a possibility that a replay desynches but nevertheless passes. But that's more a "problem" for single replays than for the huge replay base you have since for the latter it's unlikely that most of them are such corner cases.

Simon

Yes, it's always possible that tests pass despite a bug. But that's in the nature of such tests.

To mitigate, we can make more tests, or more precise tests that examine more corner cases.

-- Simon

Simon

#100
D class references may be null

D still is the langauge that produces the fewest Simon rants. Many common problems have great, short solutions in D. But, as with anything worthwhile, it has its corners to point and and rant. What everybody loves most!

Class objects live on the garbage-collected heap. The basics of OO of D are similar to OO in Java and work great. Your class variables are references (pointers, names). If you want polymorphism in any language, you need reference semantics, therefore all this is consistent, good design.

The initial value of any class reference is null. That is also a good choice.



class Color { /* ... */ }
class Porcupine {
    Color color;
// implicitly null
}

But D has no way to statically enforce that a reference cannot be null in a given context. You may assert a reference's non-null-ness, but that happens at runtime. I want nice check at compiletime. Also, it feels slightly dumb to assert for non-null because your operating system already asserts this for you on every usage: The OS will print "segmentation fault" and you run your debugger. :P

I like to write several short methods, and naturally, some of them take class arguments. I really don't want to assert that my parameters aren't null every time over and over.

Structs in D are value types, they have no such issues. If I define Point to be a struct with two ints, x and y, then Point will occupy as much memory as two ints would, and its default value is x = 0, y = 0, because 0 happens to be the default value for int. (Everything in D is default-initialized, which kills a massive class of bugs from C++. If you're absolutely sure that the default initialization kills your performance, you can explicitly write Point p = void;)

Problem statement: I wish for a class reference that is guaranteed to be non-null. Assigning null to this reference is a compile-time error, no matter how indirect the assignment was. It's an acceptable hack to have it null by default, as long as nobody can ever read/dereference it before it's overwritten with non-null for the first time.

Let's attempt a solution: I could wrap all my nullable D class references in a templated struct, such as the following, where C is the template parameter.

NotNull!C

struct NotNull(C) {
    C payload;
// we wrap a class reference
    @disable this(); // disallow default struct init with payload = null;
    this(C c) {
        assert(c !is null);
        payload = c;
    }
    alias payload this;
// allow this struct to implicitly convert to C
}

Then I can ask for a NotNull!C everywhere I'd normally ask for a C. This has 3 downsides:

  • It's a nonstandard hack. This problem is really really common in OO and it's worth a language feature, or at least a library solution. The D standard library has Nullable!S for structs, but no NotNull!C for classes.
  • It doesn't statically prevent me from instantiating the thing with null, e.g., auto x = NotNull!C(null);. It still passes compilation and only asserts at runtime. The benefit over normal class references is that it explodes already when we create the null reference, not merely when we dereference later, as a normal segfault would.
  • It's an abstraction inversion. The non-nullable type is type with simpler behavior, I can call all methods without segfault. The nullable type is the more complex type, I can either call methods on it or must check first for non-nullness. My NotNull implements a simple type in terms of a more complex type. This is bad design.
Who does it better? In Zig, your types cannot be null, not even pointers, unless you add ?, the nullable type modifier. You may not use a nullable type freely unless you specify what happens in the null case. The language has special, short constructs for exactly this problem.

In general, Zig is designed around easy passing of error conditions because it avoids exceptions and garbage collection. % is another type modifier, it means "either your type or an error code".

C# gets a mention here for its null-conditional dereferencing operator ?. that calls a method only if the reference is not null. This cures the symptom of having many ugly null checks throughout the codebase, but the original issue still stands -- it's not a compile-time prevention of assigning null to class variables.

-- Simon

ccexplore

I have to imagine that this is probably a relatively well-studied problem in computer science, and the literature probably has much more in-depth information on this topic.  Very generally speaking, static verifications by the compiler can often either mean many false positives (so you end up not really paying attention to them), or else only work in limited cases (so you still have much code where the compiler can't help you, especially the "hard" cases where it would've mattered the most).  I wonder if maybe this is difficult enough to do well (in one or both of the ways enumerated above) that for most languages (or even compiler writers, since it's probably possible to embed custom static asserts into existing language syntax like comments), it had been deemed not worth the effort?

Simon

#102
I'm not well-versed in the academics of software engineering, but yeah, I assume there should be something. And I expect other modern languages (newer than both C++ or D) to have something in this direction.

C++ would have initializer list, that would be the obvious place to assign to the non-null-ref from a guaranteed-non-null-source like new expression, other non-null-ref, STL pointer with similar guarantee.

D has no initializer lists, but the D compiler analyzes program flow in constructors, it will already error if you assign a value to const/immutable field more than once. I believe the infrastructure for non-null is similar to such const-enforcing infrastructure.

I'd be okay if you could forcefully cast from nullable reference to non-null-reference. You can already cast away D's immutable and thereby assume responsibility for any undefined behavior. Memory-safe example: Allocate memory, do something complex in it, then assign that chunk to a pointer-to-immutable. This promises that the chunk will never change, not even through other references to the same chunk. If no mutable references to this chunk escape the scope, there will be no undefined behavior, everything is perfectly fine. For this hack, there is even a standard library function, assumeUnique, which merely wraps the cast to immutable under a descriptive name. I guess offering similar hacks for subverting null would be fine.

From the linked page:
The downside of using assumeUnique's convention-based usage is that at this time there is no formal checking of the correctness of the assumption; on the upside, the idiomatic use of assumeUnique is simple and rare enough to be tolerable.

I believe D offers only nullable references out of tradition, and it would be a massive breaking change. As I said before, the non-nullable class reference to a C should be C, whereas the nullable reference is the oddity that deserves the stigmatic name C? or Nullable!C or similar.

I would love to see such a massively breaking change, yes yes, I have no qualms smashing porcelain for a small extra gain. But I'm not everybody. >_>;; And when your language is used in the industry, this might well be infeasible.

-- Simon

Simon

Lix 1v1

I want to play Lix 1v1 on highly swingy maps, where a few mistakes lose a game in one shot and then you immediately play the same map again. The games are so rapid that even though you care about winning, the actual outcome never sticks in mind for long afterwards.

geoo has always loved to play in this style, but anybody is welcome as a 1v1 opponent. I merely know that geoo shares this taste in maps. I've shied away from heavyweight multitasking maps like Chasing Upwards, but I miss that also.

Maybe the nuke-exploders should be instant for an even more rapid series of games?

Discord

Closed-source, invasive privacy policy, bloated, and slow. Use IRC for chat, public logs and a forum for archiving, and Mumble for voicechat. There is vector.im as an open-source Discord alternative but I haven't ever heard about it outside of very narrow search queries for Discord alternatives.

The massive downside of forums is that you must register for every single one separately. Sadly, github issues is not a good forum. :lix-evil:

Type popcorn

Rant from 2012, before Python got optional type annotations: Abscissa's Why I Hate Python (Or Any Dynamic Language, Really) -- A stab at type errors and missing-variable errors that a compiler would catch. The rant generated 74 comments, it's a phenomenal popcorn thread (= you can read it like you would watch an exciting action movie, munching popcorn along the way).

I still miss the non-null class refs in D. Good thing I occasionally re-read my own posts here, very instructive because the reasoning and implementation pitfalls aren't easy to keep in mind completely. >_>

-- Simon

Colorful Arty

I'd take Python Type Errors over C/C++ Segmentation faults ANY DAY. At least the Type Error tells you WHERE it happened for a quick and easy fix.