Coding: Fleeting Thoughts

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

User avatar
Flumble
Yes Man
Posts: 1945
Joined: Sun Aug 05, 2012 9:35 pm UTC

Re: Coding: Fleeting Thoughts

Postby Flumble » Fri Apr 21, 2017 11:18 pm UTC

Maybe I'm not understanding something here, but to me it seems you just made a class attribute my_component which naturally doesn't show up in an instance's attribute collection.

User avatar
The Great Hippo
Swans ARE SHARP
Posts: 6848
Joined: Fri Dec 14, 2007 4:43 am UTC
Location: behind you

Re: Coding: Fleeting Thoughts

Postby The Great Hippo » Fri Apr 21, 2017 11:35 pm UTC

Flumble wrote:Maybe I'm not understanding something here, but to me it seems you just made a class attribute my_component which naturally doesn't show up in an instance's attribute collection.
I suppose it depends on your definition of 'instance attribute'? setattr, delattr, hasattr, and getattr all behave as expected in regards to instances of MyObject and "my_component". Functionally, the only real difference is that "my_component" won't show up in any instance's __dict__ -- it shows up in the MyObject class dict, where it's a dictionary that maps instances to values.

Here's a better example, and closer to what I'm actually using:

Code: Select all

import collections


class Component(collections.UserDict):
 
  def __init__(self, name):
    super().__init__()
    self[None] = self
    self.name = name

  def __set__(self, instance, value):
    self[instance] = value

  def __get__(self, instance, cls):
    if instance in self:
      return self[instance]
    name = type(instance).__name__
    raise AttributeError("'{0}' has no attribute '{1}'".format(name, self.name))

  def __delete__(self, instance):
    del self[instance]


class Entity:
  __slots__ = tuple()
 
  def __setattr__(self, attr, value):
    if not hasattr(Entity, attr):
      setattr(Entity, attr, Component(attr))
    super().__setattr__(attr, value)

The Entity class doesn't even have an internal __dict__ (__slots__ gets rid of that); so, whenever you try to set an attribute, it checks to see if that attribute exists on its class -- and if doesn't, it adds a descriptor-dictionary as a class attribute, which then serves as a proxy for the instance's attribute. Instance attribute-values are therefore stored in a dictionary on the class, rather than the instance.

As I see it, the two primary downfalls of this setup are 1) You're mutating the class definition every time you set an attribute on one of its instances that hasn't previously been set, and 2) You're saving references of each instance every time you set an attribute. I'm addressing #1 by mutating only the top-level class (which is why __setattr__ refers to "Entity", rather than type(self)), allowing me to override these mutations explicitly by subclassing Entity -- and I'm going to address #2 by having Components store proxies of instances instead of the instances themselves.

EDIT: I'm doing this as part of an Entity-Component scheme. "my_component" gets to be a fully-functional attribute for Entity instances... and I now have a dictionary that tells me of every Entity instance that has set "my_component" to a value. This becomes super-helpful when it's time to iterate over all Entity instances that have a "my_component" attribute.

User avatar
Flumble
Yes Man
Posts: 1945
Joined: Sun Aug 05, 2012 9:35 pm UTC

Re: Coding: Fleeting Thoughts

Postby Flumble » Sat Apr 22, 2017 12:54 pm UTC

Ah, alright, I focused on the part
Now instances of 'MyObject' will have a 'my_component' attribute that isn't actually inside of the instance's internal __dict__. Which, maybe doesn't sound useful, but is actually precisely what I needed.

thinking you thought my_component is an instance attribute through some magic. :oops:

Now I also see why you'd subclass dict: to include a name attribute and have more descriptive errors. Speaking of which: maybe you also want to throw an error if some code tries to access an element that is not an object (to catch code where you accidentally use an index or a null value) (and not have component[None] = component).

User avatar
The Great Hippo
Swans ARE SHARP
Posts: 6848
Joined: Fri Dec 14, 2007 4:43 am UTC
Location: behind you

Re: Coding: Fleeting Thoughts

Postby The Great Hippo » Sat Apr 22, 2017 11:31 pm UTC

Flumble wrote:Now I also see why you'd subclass dict: to include a name attribute and have more descriptive errors. Speaking of which: maybe you also want to throw an error if some code tries to access an element that is not an object (to catch code where you accidentally use an index or a null value) (and not have component[None] = component).
Yeah, component[None] = component was kind of dumb -- I put it there so I could access components at the class level easily, but there's a much better (and explicit) way to accomplish that.

Re: throwing an error, though -- I actually think I want to map indexes to values -- rather than instances to values. Otherwise, I'm creating references to these instances silently. I could use a WeakKeyDictionay, but weakref dicts are dangerous -- if you're not careful, they can change sizes unexpectedly during iteration. And since I'm going to be doing a lot of different things with components, it seems safer to expand the Entity class so that each instance has its own internal index -- and components map these indexes rather than the entities themselves.

To do this, I'm using a special 'ComponentDict' class as a helper for Component -- it acts like an ordinary dictionary, but uses a 'translate_key' staticmethod to translate the keys it receives. These translations default to just returning the supplied key, but the static function can be overwritten to do another type of translation... which is what "Entity" does by subclassing Component (replacing the default "translate_key" with one that retrieves the Entity instance's internal index... or 'eid').

Code: Select all

import collections
import operator


class ComponentDict(collections.UserDict):
  translate_key = staticmethod(lambda x: x)

  def __setitem__(self, item, value):
    key = self.translate_key(item)
    super().__setitem__(key, value)

  def __getitem__(self, item):
    key = self.translate_key(item)
    return super().__getitem__(key)

  def __delitem__(self, item):
    key = self.translate_key(item)
    super().__delitem__(key)

  def __contains__(self, item):
    key = self.translate_key(item)
    return super().__contains__(key)

  def __iter__(self):
    for item in self.data:
      yield self.translate_key(item)


class Component(ComponentDict):

  def __init__(self, attr):
    super().__init__()
    if not isinstance(attr, str):
      name = type(attr).__name__
      raise TypeError("attribute name must be string, not {}".format(name))
    self.attr = attr

  def __set__(self, instance, value):
    self[instance] = value

  def __get__(self, instance, cls):
    if instance is None:
      return self
    if instance not in self:
      name = type(instance).__name__
      raise AttributeError("'{0}' has no attribute '{1}'".format(name, self.attr))
    return self[instance]


class Entity:
  __slots__ = ("eid",)

  class Component(Component):
    translate_key = staticmethod(operator.attrgetter("eid"))

  def __init__(self, eid):
    self.eid = eid

  def __setattr__(self, attr, value):
    try:
      super().__setattr__(attr, value)
    except AttributeError:
      # We explicitly refer to Entity, rather than type(self), because
      # we want to ensure that only the top-level class (Entity) gets
      # mutated.
      setattr(Entity, attr, Entity.Component(attr))
      super().__setattr__(attr, value)
My next goal is to modify Entity a little so it generates eids automatically -- rather than having to supply them.

User avatar
Xenomortis
Not actually a special flower.
Posts: 1397
Joined: Thu Oct 11, 2012 8:47 am UTC

Re: Coding: Fleeting Thoughts

Postby Xenomortis » Wed Apr 26, 2017 9:54 am UTC

I am looking at my future and it's all "reallyFuckingLongNamesManager" and "noIdeaWhatThisThingIsComponent.getOtherThingSource(what_is_this)" and my eyes just glaze over...
Image

User avatar
ucim
Posts: 5568
Joined: Fri Sep 28, 2012 3:23 pm UTC
Location: The One True Thread

Re: Coding: Fleeting Thoughts

Postby ucim » Wed Apr 26, 2017 1:19 pm UTC

Xenomortis wrote:I am looking at my future and it's all "reallyFuckingLongNamesManager" and "noIdeaWhatThisThingIsComponent.getOtherThingSource(what_is_this)" and my eyes just glaze over...


There are only two hard things in programming...

Jose
Order of the Sillies, Honoris Causam - bestowed by charlie_grumbles on NP 859 * OTTscar winner: Wordsmith - bestowed by yappobiscuts and the OTT on NP 1832 * Ecclesiastical Calendar of the Order of the Holy Contradiction * Please help addams if you can. She needs all of us.

User avatar
Yakk
Poster with most posts but no title.
Posts: 11045
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: Coding: Fleeting Thoughts

Postby Yakk » Wed Apr 26, 2017 3:33 pm UTC

Cache invalidation, naming things, and off-by-one errors.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

User avatar
Xenomortis
Not actually a special flower.
Posts: 1397
Joined: Thu Oct 11, 2012 8:47 am UTC

Re: Coding: Fleeting Thoughts

Postby Xenomortis » Wed Apr 26, 2017 3:51 pm UTC

Descriptive variable name my fucking arse.

To be honest though, the code isn't "bad" - I just hate the dense prose-like code overly long names result in.
And getters.
I fucking hate "getX" methods.
Image

User avatar
Flumble
Yes Man
Posts: 1945
Joined: Sun Aug 05, 2012 9:35 pm UTC

Re: Coding: Fleeting Thoughts

Postby Flumble » Wed Apr 26, 2017 3:53 pm UTC

ISSUE: Cache invalidation is hard
WORKAROUND: Assume all caches are dirty all the time

ISSUE: Naming things is hard
WORKAROUND: Name each new variable <globally unique contributor name><unix time> where <unix time> is the time value at the time of writing. (be sure to sleep for 1 second after naming the variable)



Anyway, what's a good way to collect errors/exceptions/warnings in a java* program? Logger seems great for logging stuff in the wild, but how would I capture the messages in junit tests?

*it's a school assignment

User avatar
You, sir, name?
Posts: 6974
Joined: Sun Apr 22, 2007 10:07 am UTC
Location: Chako Paul City
Contact:

Re: Coding: Fleeting Thoughts

Postby You, sir, name? » Wed Apr 26, 2017 8:14 pm UTC

Flumble wrote:Anyway, what's a good way to collect errors/exceptions/warnings in a java* program? Logger seems great for logging stuff in the wild, but how would I capture the messages in junit tests?


Code against a logger interfacee, and inject a different implementation in the test set-up which stores exceptions.
I edit my posts a lot and sometimes the words wrong order words appear in sentences get messed up.

Tub
Posts: 311
Joined: Wed Jul 27, 2011 3:13 pm UTC

Re: Coding: Fleeting Thoughts

Postby Tub » Wed Apr 26, 2017 8:18 pm UTC

Flumble wrote:WORKAROUND: Name each new variable <globally unique contributor name><unix time> where <unix time> is the time value at the time of writing. (be sure to sleep for 1 second after naming the variable)

I'd ask you not to write code during leap seconds. But with your knowledge of unix time, I guess your computer will crash during leap seconds anyway.

Flumble wrote:Anyway, what's a good way to collect errors/exceptions/warnings in a java* program? Logger seems great for logging stuff in the wild, but how would I capture the messages in junit tests?

Shouldn't any proper logger class have the option to log into an array of strings or something? Or at least provide a callback when logging, so you can append it to the array yourself?

User avatar
phlip
Restorer of Worlds
Posts: 7543
Joined: Sat Sep 23, 2006 3:56 am UTC
Location: Australia
Contact:

Re: Coding: Fleeting Thoughts

Postby phlip » Thu Apr 27, 2017 12:54 am UTC

Seen floating around Twitter: Compiling C to x86 using only printable ASCII.

Learn how to program a computer when you aren't allowed to use MOV. Or backward jumps.

Code: Select all

enum ಠ_ಠ {°□°╰=1, °Д°╰, ಠ益ಠ╰};
void ┻━┻︵​╰(ಠ_ಠ ⚠) {exit((int)⚠);}
[he/him/his]

User avatar
jaap
Posts: 2072
Joined: Fri Jul 06, 2007 7:06 am UTC
Contact:

Re: Coding: Fleeting Thoughts

Postby jaap » Thu Apr 27, 2017 7:22 am UTC

Microsoft Powerpoint is Turing complete:
https://www.youtube.com/watch?v=uNjxe8ShM-8

User avatar
You, sir, name?
Posts: 6974
Joined: Sun Apr 22, 2007 10:07 am UTC
Location: Chako Paul City
Contact:

Re: Coding: Fleeting Thoughts

Postby You, sir, name? » Thu Apr 27, 2017 1:02 pm UTC

phlip wrote:Seen floating around Twitter: Compiling C to x86 using only printable ASCII.

Learn how to program a computer when you aren't allowed to use MOV. Or backward jumps.


Can program in only MOV too

https://github.com/xoreaxeaxeax/movfuscator
I edit my posts a lot and sometimes the words wrong order words appear in sentences get messed up.

User avatar
Link
Posts: 1327
Joined: Sat Mar 07, 2009 11:33 am UTC
Location: ᘝᓄᘈᖉᐣ
Contact:

Re: Coding: Fleeting Thoughts

Postby Link » Fri Apr 28, 2017 8:30 pm UTC

phlip wrote:
Seen floating around Twitter: Compiling C to x86 using only printable ASCII.

Learn how to program a computer when you aren't allowed to use MOV. Or backward jumps.
You, sir, name? wrote:
Can program in only MOV too

https://github.com/xoreaxeaxeax/movfuscator

Image

In other "ugly side of programming" news: I made a class that makes heavy use of std::shared_ptr<void>-types. All the ugliness is well-hidden from anything reasonable that ever interacts with the class and everything is type-safe in the end, but I still feel a little dirty. All things considered, I think I could have built it around boost::any instead, but I didn't think of that until well after I'd started, and I don't really want to add Boost as a dependency at this point anyway. (I might overhaul this entire thing if I ever decide I really need Boost after all, or if something gnarly unexpectedly pops up because of the use of void pointers.)

Tub
Posts: 311
Joined: Wed Jul 27, 2011 3:13 pm UTC

Re: Coding: Fleeting Thoughts

Postby Tub » Sat Apr 29, 2017 9:12 pm UTC

Link wrote:In other "ugly side of programming" news: I made a class that makes heavy use of std::shared_ptr<void>-types.

Hang on.. are you screwing around with a custom deleter and (possibly) an enum for type tracking during dereferencing? What advantage does this have over, say, a base class with a virtual destructor? Do you need to track atomic types or something?

btw, c++17 has std::any

>-)
Posts: 512
Joined: Tue Apr 24, 2012 1:10 am UTC

Re: Coding: Fleeting Thoughts

Postby >-) » Mon May 01, 2017 2:56 am UTC

just to eek that last bit of performance out of python, i manually inlined all my helper functions into one single monstrous blob of code. just about doubled the speed too, which is way more than i expected, but it still wasn't good enough, so i had to rewrite it in c++

User avatar
Link
Posts: 1327
Joined: Sat Mar 07, 2009 11:33 am UTC
Location: ᘝᓄᘈᖉᐣ
Contact:

Re: Coding: Fleeting Thoughts

Postby Link » Mon May 01, 2017 6:58 am UTC

Tub wrote:
Link wrote:In other "ugly side of programming" news: I made a class that makes heavy use of std::shared_ptr<void>-types.

Hang on.. are you screwing around with a custom deleter and (possibly) an enum for type tracking during dereferencing? What advantage does this have over, say, a base class with a virtual destructor? Do you need to track atomic types or something?

btw, c++17 has std::any

Something like that. The class basically boils down to something like a souped-up map where the type of the value is given at insertion using templated functions, and tracked using std::type_index. Storage space for data is allocated using appropriately typed smart pointer, then cast to a shared void pointer to put it in an underlying map, and cast back to its appropriate type when requested; it basically involves a templated at method that checks if the template type matches the stored type. If it does, it casts the void pointer to a smart pointer to whatever is requested and returns it, and if not, it throws a runtime error. I basically want this to be able to store any type, including POD types, without requiring the storable types to be known at class instantiation.

C++17 is looking good, but it's not even finalised yet, so I think it'll be a while before it reaches the stage that it's relatively well-supported by most compilers.

User avatar
Yakk
Poster with most posts but no title.
Posts: 11045
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: Coding: Fleeting Thoughts

Postby Yakk » Tue May 02, 2017 2:26 pm UTC

You can write your own `any` rather than rolling your own distinct type with similar purpose.

Code: Select all

    struct simple_any_sp {
      std::shared_ptr<void> ptr;
      const std::type_info* info = 0;
      template<class T>
      simple_any_sp(std::shared_ptr<T> t):
        ptr( t ),
        info( &typeid( T ) )
      {}
      simple_any_sp() noexcept =default;
      simple_any_sp(simple_any_sp const&) noexcept =default;
      simple_any_sp(simple_any_sp && o) noexcept :ptr(std::move(o).ptr), info(o.info) { o.info = 0; }
      simple_any_sp& operator=(simple_any_sp const&) noexcept = default;
      simple_any_sp& operator=(simple_any_sp && o) noexcept {
        ptr = std::move(o).ptr;
        info = o.info;
        o.info = 0;
        return *this;
      }
      template<class T>
      std::shared_ptr<T> get() const {
        if (!info) return {};
        if (&typeid(T) != info && std::typeindex(typeid(T)) != std::typeindex(*info))
          return {};
        return {static_cast<T*>(ptr.get()), ptr};
      }
    };

off the top of my head, the above should be a simple any-shared-pointer wrapper. No exceptions involved.

Code: Select all

simple_any_sp sp = std::make_shared<int>(7);

auto pdouble = sp.get<double>();
Assert(!pdouble);
auto pint = sp.get<int>();
Assert(pint);
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

Tub
Posts: 311
Joined: Wed Jul 27, 2011 3:13 pm UTC

Re: Coding: Fleeting Thoughts

Postby Tub » Tue May 02, 2017 2:40 pm UTC

Or you could just grab an std::any implementation; it'll likely compile in c++11, too. And when you switch compilers, you just switch to std::any and remove your code, because you've already used the correct API.

btw, std::any allows casting to any compatible type, not just to the exact type entered. e.g.

Code: Select all

simple_any_sp p( std::make_shared<std::string>("Hello world") );
auto s = p.get<const std::string>()

would fail in Yakk's implementation, while std::any does the proper cast.

User avatar
Yakk
Poster with most posts but no title.
Posts: 11045
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: Coding: Fleeting Thoughts

Postby Yakk » Tue May 02, 2017 6:04 pm UTC

No, that should work; typeid ignores const.

Storing a `shared_ptr<const T>` won't work, however, as it isn't implicitly convertible to `shared_ptr<void>`.

I would expect most std::any implementations to use C++14, so would require patching.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

User avatar
Link
Posts: 1327
Joined: Sat Mar 07, 2009 11:33 am UTC
Location: ᘝᓄᘈᖉᐣ
Contact:

Re: Coding: Fleeting Thoughts

Postby Link » Wed May 03, 2017 8:44 pm UTC

Yakk wrote:
You can write your own `any` rather than rolling your own distinct type with similar purpose.

[snip]
off the top of my head, the above should be a simple any-shared-pointer wrapper. No exceptions involved.
That's actually not entirely dissimilar to what I've done (albeit more elegant), although you simply return an empty pointer where I would throw an exception. As far as I'm concerned, being too lax about types is an error, and I prefer throwing exceptions over silently failing: the latter can make debugging a much bigger pain. The "Aborted (core dumped)" you get from an uncaught exception may be a bit harsh, but it beats scenarios like getting an empty pointer, mindlessly passing it around to other methods, and then getting unexpected behaviour thirty function calls deep because you mistakenly typed get<double> instead of get<int> a few hundred lines back and never bothered to check if that went OK. And given the "code horror" stories I've heard over the years, it is not only possible but practically inevitable that someone hits such a scenario eventually. Besides, wrapping get in a try-catch block isn't really any harder than checking for an empty pointer.

User avatar
Yakk
Poster with most posts but no title.
Posts: 11045
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: Coding: Fleeting Thoughts

Postby Yakk » Thu May 04, 2017 6:06 pm UTC

All pointers can be null. They are a nullable type.

Trying to guard with external logic that some pointers cannot be null is a trying to enforce a type constraint without using the type system.

Guarding a pointer with an exception if it is null just teaches you not to accept that all pointers can be null.

You could write a shared reference, which is a reference to a shared value which cannot be null. That would be a different type. Maintaining that guarantee may require certain sacrifices, like the ability to both default construct it and have the ability to have a nothrow move, for example.

Half-assed type constraints, like "I hereby assume this (smart) pointer is not null", are a plague. Your "throw if you don't return a non-null pointer" looks like trying to enforce that.

You can have a convert-to-reference that *does* throw, but you'll note this is converting to a reference. And not a smart reference (unless you write one, which isn't quite possible in C++ right now without comprimise). So lifetime concerns now remain.

Embrance nullability. If a type is nullalbe, code must treat it as such.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

User avatar
Link
Posts: 1327
Joined: Sat Mar 07, 2009 11:33 am UTC
Location: ᘝᓄᘈᖉᐣ
Contact:

Re: Coding: Fleeting Thoughts

Postby Link » Fri May 05, 2017 10:43 am UTC

I fully agree that you should generally account for pointers being null, but in languages with an exception mechanism, I just don't think errors are legitimate reasons to return null pointers. Especially in the case of an any-pointer, I think it's a very reasonable requirement that, barring hardware failure, any call to get that executes immediately after assignment of the pointer (...from the same thread, yada yada...) either returns a pointer to the same data as the assignment, or fails to return entirely. The most reasonable way to guarantee that, is to return a null pointer if and only if the internal void pointer is also null, and to throw an exception if there is no "safe" way to return a pointer of the requested type to the void pointer's data.

Enforcing type constraints without (properly) using the type mechanism is also something I'm not necessarily opposed to, if the type mechanism has shortcomings that make it too inflexible for a particular use case. Ideally I would need neither RTTI nor templates for strongly-typed "plain old storage" of an arbitrary number of objects of various types, but that's not something the C++11/14 standards provide yet. Ergo kludges.

Tub
Posts: 311
Joined: Wed Jul 27, 2011 3:13 pm UTC

Re: Coding: Fleeting Thoughts

Postby Tub » Fri May 05, 2017 8:04 pm UTC

Yakk wrote:All pointers can be null. They are a nullable type.

Whoa. Are you really arguing that APIs must not make any guarantees except returning the correct type?

Are you also opposed to sort() returning a sorted array? Will you argue that arrays are an unsorted type, and it's valid for sort() to return an unsorted array, and it's the caller's job to handle that?

The whole point of functions or methods is to put objects into well defined states. "Being sorted" is a well-defined state of arrays, and "pointing to a valid object" is a well defined state of a pointer type, and it's absolutely ok for an API to make either guarantee.

Yakk wrote:Half-assed type constraints, like "I hereby assume this (smart) pointer is not null", are a plague. Your "throw if you don't return a non-null pointer" looks like trying to enforce that.

That's exactly what the stdlib does, though. There are plenty of functions that guarantee that the returned type can be dereferenced. For examples with pointers, see "new", "make_shared" or "make_unique". Or look at the containers to find methods guaranteed to return iterators that can be dereferenced.


Even if you come to the conclusion that c++ commitee is wrongtm about these things, you should still stick to the convention. Principle of least surprise and everything.

User avatar
Qaanol
The Cheshirest Catamount
Posts: 3035
Joined: Sat May 09, 2009 11:55 pm UTC

Re: Coding: Fleeting Thoughts

Postby Qaanol » Fri May 05, 2017 8:56 pm UTC

Tub wrote:Are you also opposed to sort() returning a sorted array? Will you argue that arrays are an unsorted type, and it's valid for sort() to return an unsorted array, and it's the caller's job to handle that?

Well if you’re working with an array of IEEE–754 floating-point values, some of which are NaN, and “sort()” uses the “<” operator…
wee free kings

User avatar
Yakk
Poster with most posts but no title.
Posts: 11045
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: Coding: Fleeting Thoughts

Postby Yakk » Sat May 06, 2017 5:55 pm UTC

No, I'm saying that treating a nullable type as non-nullable is a bad idea.

Passing arrays around and assuming they are sorted is dangerous. If you want a sorted array to pass around, have a type that enforces its sortedness (or tracks if it is sorted). Even that is dangerous, as it takes one place to break the invariant and a million places where you keep it, unless access is very constrained.

A function that provides extra guarantees on the value returned, sure. But this is honestly of limited usefulness; outside of the immediate context of the return of the function, that assumption shouldn't be made.

Exceptions should be used in exceptional circumstances. They significant costs both on code written (as it now has to be robust against come-from calls). Unless you write your code to experience random exceptions and survive (like Chaos Monkey), exceptional code paths are going to be rarely exercised and hence far more fragile and less robust. Come-from is *really hard to reason about* reliably, and that is what exceptions reduce to.

A maybe-null type is far easier to reason about.

Dereferencing null pointers that code assumed it could never be null is far too common for me to glibly say it is a good idea.

Even with sorting, if your code would execute undefined behavior if a list isn't sorted, I'd want to have checked its sorted state *right there*, at the very least with an Assert in debug mode.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

Tub
Posts: 311
Joined: Wed Jul 27, 2011 3:13 pm UTC

Re: Coding: Fleeting Thoughts

Postby Tub » Mon May 08, 2017 2:24 pm UTC

Nobody said anything about passing a long-lived object around and expecting invariants to be maintained by pixie magic. The example was about guaranteeing an invariant on the return value of a single function.

What does or does not constitute an "exception" could be debated for hours, so instead I'll make two points:
* The surrounding code must be exception safe anyway, since get<T> can throw std::bad_alloc (and maybe others). If your code isn't exception safe, then that's already a bug. RAII isn't that difficult.
* stdlib usually throws on getters where the requested item/value cannot be provided. The methods that don't throw are usually called find(). Consistency ftw.


You're makeing a habit of using null pointers, and then you're complaining about null pointer errors being "far too common". They're not common for me, and that may be because I don't return null pointers, I don't pass null pointers (unless it's an optional argument which defaults to nullptr), and if I must have a null pointer, then it's either a local variable (e.g. default constructed, then initialized a few lines later) or a private member (e.g. pointer to a helper class or singleton that's initialized on demand). I can't remember the last time I had a null pointer bug. Reducing the places where you need to type the assert() reduces the likelyhood to forget one.

User avatar
Yakk
Poster with most posts but no title.
Posts: 11045
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: Coding: Fleeting Thoughts

Postby Yakk » Mon May 08, 2017 2:52 pm UTC

The assert guards against failure that it shouldn't be null.

I'm coming from a many million line code base written by generations of programmers (aka, a real code base!). The assumption that something is never null is never safe. When code succeeds, it gets larger and lasts far longer than you can ever believe.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

User avatar
Thesh
Made to Fuck Dinosaurs
Posts: 5497
Joined: Tue Jan 12, 2010 1:55 am UTC
Location: Colorado

Re: Coding: Fleeting Thoughts

Postby Thesh » Sat May 20, 2017 6:34 pm UTC

I decided I hate python. Seriously, randint and rangerange both do practically the exact same thing, except one is inclusive and one is exclusive when it comes to the upper bound. That's the only difference in the functions, and here's the documentation for randrange:

Return a randomly selected element from range(start, stop, step). This is equivalent to choice(range(start, stop, step)), but doesn’t actually build a range object.


And the definition for choice:

Return a random element from the non-empty sequence seq. If seq is empty, raises IndexError.


So you need to look at the documentation for three function to figure out if it returns an inclusive or exclusive range. Contrast with randint:

Return a random integer N such that a <= N <= b.


Could anyone find any reason at all why you would write a simple, concise description of that function, and not just write this for randrange which is right next to it:

Return a random integer N such that a <= N < b.


Granted, it's better than no documentation.
Honesty replaced by greed, they gave us the reason to fight and bleed
They try to torch our faith and hope, spit at our presence and detest our goals

User avatar
Xenomortis
Not actually a special flower.
Posts: 1397
Joined: Thu Oct 11, 2012 8:47 am UTC

Re: Coding: Fleeting Thoughts

Postby Xenomortis » Sat May 20, 2017 7:17 pm UTC

Thesh wrote:Could anyone find any reason at all why you would write a simple, concise description of that function, and not just write this for randrange which is right next to it:

Return a random integer N such that a <= N < b.

Because it's not accurate.
The declaration for randrange is

Code: Select all

def randrange(start, stop, step)


It is literally "return a random element from a range", where "range" means "range(start, stop, step)". This is significant when step != 1.
Image

User avatar
Thesh
Made to Fuck Dinosaurs
Posts: 5497
Joined: Tue Jan 12, 2010 1:55 am UTC
Location: Colorado

Re: Coding: Fleeting Thoughts

Postby Thesh » Sat May 20, 2017 7:30 pm UTC

Step is an optional parameter, and that's completely irrelevant to the point (i.e. the statement is true regardless of whether you include the step parameter).
Honesty replaced by greed, they gave us the reason to fight and bleed
They try to torch our faith and hope, spit at our presence and detest our goals

User avatar
ucim
Posts: 5568
Joined: Fri Sep 28, 2012 3:23 pm UTC
Location: The One True Thread

Re: Coding: Fleeting Thoughts

Postby ucim » Sun May 21, 2017 6:49 pm UTC

What's the point of the switch statement? On the surface it seems like a neat and clean way to choose between options, but it is actually more limiting and costs an extra layer of indent. Falling through (a case statement) is a nice option, but is also a sneaky place to breed unexpected bugs.

I do use switch statements, but wonder if that's really the best approach.

Jose
Order of the Sillies, Honoris Causam - bestowed by charlie_grumbles on NP 859 * OTTscar winner: Wordsmith - bestowed by yappobiscuts and the OTT on NP 1832 * Ecclesiastical Calendar of the Order of the Holy Contradiction * Please help addams if you can. She needs all of us.

User avatar
ahammel
My Little Cabbage
Posts: 2135
Joined: Mon Jan 30, 2012 12:46 am UTC
Location: Vancouver BC
Contact:

Re: Coding: Fleeting Thoughts

Postby ahammel » Sun May 21, 2017 7:44 pm UTC

ucim wrote:What's the point of the switch statement? On the surface it seems like a neat and clean way to choose between options, but it is actually more limiting and costs an extra layer of indent. Falling through (a case statement) is a nice option, but is also a sneaky place to breed unexpected bugs.

I do use switch statements, but wonder if that's really the best approach.

Jose

I favour pattern matching. Failing that, nested ternary operators can go a long way if they're formatted well.
He/Him/His/Alex
God damn these electric sex pants!

User avatar
Flumble
Yes Man
Posts: 1945
Joined: Sun Aug 05, 2012 9:35 pm UTC

Re: Coding: Fleeting Thoughts

Postby Flumble » Sun May 21, 2017 9:07 pm UTC

ucim wrote:costs an extra layer of indent.

I usually indent the case x: line to the same level as the switch (x) {. Some IDEs apply this code style as a default too (as well as the public: and other access specifiers when working in C++).
Wholly agree that the way switch is constructed in C (and derived languages with the same construction) is horrible: fall-through makes the code (flow) hard to read, you're limited to primitive types and the cases are limited to constant expressions.

If you switch to a more functional or more modern language, you'll see the beauty of switch/case/select statements expressions. :)


Thesh wrote:Step is an optional parameter, and that's completely irrelevant to the point (i.e. the statement is true regardless of whether you include the step parameter).

It's totally relevant, since a step greater than 1 means it doesn't pick any integer in the range, only k*step+start. And a negative step results in stop < N <= start, so lower bound exclusive, upper bound inclusive. Luckily, the (online) manual includes a link to range() where the behaviour of the arguments is explained, with examples. (Good thing too, because there's no sentence explaining "without step specified and using numbers, it will yield a 'list' of integers start <= N < stop".)
Moreover, randrange, because of range, supports any type with an __index__ method as start and stop arguments. Despite claiming to be equal to choice(range(args)), randrange doesn't support types with an __index__ method. :|

Tub
Posts: 311
Joined: Wed Jul 27, 2011 3:13 pm UTC

Re: Coding: Fleeting Thoughts

Postby Tub » Sun May 21, 2017 9:30 pm UTC

ucim wrote:What's the point of the switch statement?

DO switch on enums. At least in C/C++, the compiler may helpfully complain if you forgot a value.
MAYBE switch on strings, when you're parsing enum-like user input, and your language supports it.
DON'T switch on numeric values, unless your language supports range-expressions on case statements.
NEVER switch on floats. Don't do equality checks on floats.

Today, I'd count the possibility of fallthrough-tricks against switch. Duff's device is largely obsoleted by advances in compilers, and any other use of fallthroughs is unlikely to improve either performance or code readability. Especially if you count the required 37 lines of comments explaining why the fallthrough is not a bug, and the other 29 lines of comments trying to silence your code checker.

speising
Posts: 2069
Joined: Mon Sep 03, 2012 4:54 pm UTC
Location: wien

Re: Coding: Fleeting Thoughts

Postby speising » Sun May 21, 2017 10:47 pm UTC

That may all well be, but then you encounter the switch in c# , that *doesn't* allow fall through (except for empty cases), but still requires a completely superfluous "break;" at the eend of each case (including the last).

User avatar
Thesh
Made to Fuck Dinosaurs
Posts: 5497
Joined: Tue Jan 12, 2010 1:55 am UTC
Location: Colorado

Re: Coding: Fleeting Thoughts

Postby Thesh » Sun May 21, 2017 10:59 pm UTC

Code: Select all

namespace ConsoleApplication1
{
   enum Colors
   {
      Red, Green, Blue, Orange, Yellow, Purple

   };
   class Program
   {
      static void Main(string[] args)
      {
         Colors color = Colors.Red;
         switch (color)
         {
            case Colors.Red:
               Console.WriteLine("RED!!!");
               goto case Colors.Green;
            case Colors.Green:
            case Colors.Blue:
               Console.WriteLine("Primary");
               break;
            default:
               Console.WriteLine("Secondary");
               return;
         }
         return;
      }
   }
}
Honesty replaced by greed, they gave us the reason to fight and bleed
They try to torch our faith and hope, spit at our presence and detest our goals

speising
Posts: 2069
Joined: Mon Sep 03, 2012 4:54 pm UTC
Location: wien

Re: Coding: Fleeting Thoughts

Postby speising » Sun May 21, 2017 11:14 pm UTC

Hm. ok, i didn't know about the goto case, but the break seems still unnecessary.

User avatar
ucim
Posts: 5568
Joined: Fri Sep 28, 2012 3:23 pm UTC
Location: The One True Thread

Re: Coding: Fleeting Thoughts

Postby ucim » Sun May 21, 2017 11:20 pm UTC

@Thesh: What about:

$colors = array('red', 'green', 'blue', 'orange', 'yellow', 'purple');
$primary = array('red', 'green', 'blue');
$color = pickone($colors);

if ($color == 'red')
{ echo "RED!!!\n";
}
if (in_array($color, $primary))
{ echo 'Primary';
}
else
{ echo 'Secondary';
}


Other than the sheer joy of using a goto inside a switch (poke my eyes out please!), what's the advantage of your version?

Jose
Order of the Sillies, Honoris Causam - bestowed by charlie_grumbles on NP 859 * OTTscar winner: Wordsmith - bestowed by yappobiscuts and the OTT on NP 1832 * Ecclesiastical Calendar of the Order of the Holy Contradiction * Please help addams if you can. She needs all of us.


Return to “Coding”

Who is online

Users browsing this forum: No registered users and 11 guests