Re: [Python-ideas] Forward-References & Out-of-order declaration

Just to provide a concrete example, sqlalchemy's ORM seems to really contort itself (at least from the user's perspective) to get around this problem. The reason in that case is that the dependencies between tables don't have to be directed acyclic graphs, e.g. it's common for two tables to depend on each other. I've also run into this problem when working with my home-grown message-passing APIs, which can also form more complicated dependency graphs. So I do think that good code occasionally has to warp itself to fit into python's model. -Kale

On Thu, Aug 27, 2015 at 3:15 AM, Kale Kundert <kale@thekunderts.net> wrote:
Fair point, but in my experience with SQLAlchemy, it's not that bad to identify tables with string identifiers: class Manufacturer(Base): __tablename__ = 'manufacturers' id = Column(Integer, primary_key=True) name = Column(String, nullable=False) stuff = relationship("Thing", backref="manufacturer") class Thing(Base): __tablename__ = 'things' id = Column(Integer, primary_key=True) name = Column(String, nullable=False) makerid = Column(Integer, ForeignKey('manufacturer.id'), nullable=False) (I might have the specifics a bit wrong, but it's something like this.)
Same sort of thing. You're right that these violate (by necessity) the principle of "first use is definition", but these are incredibly rare cases. Just in the example above, and without any real code doing any real work, I have seven names: Manufacturer, Base, Column, Integer, relationship, Thing, String Two of them have a cyclic relationship (Manufacturer and Thing). All of the rest can still follow that principle (and in this case, most of them would be listed in the top-of-file import block). There are specific situations where the graph is more complicated, but I still like being confident that the code will broadly follow that design layout. The two basic solutions still apply: either use string names to identify not-yet-defined objects, or have a "pre-declare" syntax to make things possible. In C, "struct foo;" is enough to let you declare pointers to foo; in Python, you could have "Thing = Table()" prior to defining Manufacturer, and then you could use an unquoted Thing to define the relationship. Either way makes it clear that something unusual is happening. ChrisA

On 27 August 2015 at 09:41, Chris Angelico <rosuav@gmail.com> wrote:
It's also the case that *circular dependencies hint at a design problem*. They're sometimes an unavoidable problem (because you're modelling a genuinely bidirectional relationship), but they're still a problem, since acyclic models structurally avoid a *lot* of the challenges that come up when cycles may be present (for example, consider how much easier it is to traverse a filesystem tree if you *don't* support following symlinks). Teasing apart a data model (which is what a class hierarchy represents) to either eliminate the circular references, or else limit them to within particular files is actually a pretty good way to figure out which parts of that model are tightly coupled, and which are more loosely related. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Thu, Aug 27, 2015 at 3:15 AM, Kale Kundert <kale@thekunderts.net> wrote:
Fair point, but in my experience with SQLAlchemy, it's not that bad to identify tables with string identifiers: class Manufacturer(Base): __tablename__ = 'manufacturers' id = Column(Integer, primary_key=True) name = Column(String, nullable=False) stuff = relationship("Thing", backref="manufacturer") class Thing(Base): __tablename__ = 'things' id = Column(Integer, primary_key=True) name = Column(String, nullable=False) makerid = Column(Integer, ForeignKey('manufacturer.id'), nullable=False) (I might have the specifics a bit wrong, but it's something like this.)
Same sort of thing. You're right that these violate (by necessity) the principle of "first use is definition", but these are incredibly rare cases. Just in the example above, and without any real code doing any real work, I have seven names: Manufacturer, Base, Column, Integer, relationship, Thing, String Two of them have a cyclic relationship (Manufacturer and Thing). All of the rest can still follow that principle (and in this case, most of them would be listed in the top-of-file import block). There are specific situations where the graph is more complicated, but I still like being confident that the code will broadly follow that design layout. The two basic solutions still apply: either use string names to identify not-yet-defined objects, or have a "pre-declare" syntax to make things possible. In C, "struct foo;" is enough to let you declare pointers to foo; in Python, you could have "Thing = Table()" prior to defining Manufacturer, and then you could use an unquoted Thing to define the relationship. Either way makes it clear that something unusual is happening. ChrisA

On 27 August 2015 at 09:41, Chris Angelico <rosuav@gmail.com> wrote:
It's also the case that *circular dependencies hint at a design problem*. They're sometimes an unavoidable problem (because you're modelling a genuinely bidirectional relationship), but they're still a problem, since acyclic models structurally avoid a *lot* of the challenges that come up when cycles may be present (for example, consider how much easier it is to traverse a filesystem tree if you *don't* support following symlinks). Teasing apart a data model (which is what a class hierarchy represents) to either eliminate the circular references, or else limit them to within particular files is actually a pretty good way to figure out which parts of that model are tightly coupled, and which are more loosely related. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
participants (3)
-
Chris Angelico
-
Kale Kundert
-
Nick Coghlan