Live data from Hacker News

Cyclic dependencies are evil (2013)

fsharpforfunandprofit.com

101–110 of 125 posts

Re: Cyclic dependencies are evil (2013)

#101

Earlier quoted context omitted.

If the code is implementing against interfaces, why would late binding mess up your IDE experience at all?

My heart sinks whenever I need to spend ten minutes trying to work out which of the three different implementors is actually going to be called on a particular code path. For about a week of December, my work was solely spelunking to track down which implementations of an interface in C# were dead code and so on.

Right. Often there are 2 types of interfaces in a project. The first are "natural" interfaces, that you have put some design into and are meant to be reusable. Things like Streams or Collections. When you write a function that uses one of these, you really are expecting to be able to use any implementation. If you want to go to the implementation of Stream.Read, obviously the IDE isn't going to be able to do it, it's abstract and there are any number of implementations.

You get the other kind of interface when you want to loosely-couple your code, and so you define interfaces for many classes. Often, there is only a single class that implements the interface in your project, though there may be mock implementations in your test code. Even though there is a single "real" implementation, the IDE can't/won't jump to that implementation in the same way it won't in the first case. This is frustrating though, because it would have worked if you hadn't extracted the interface for improved testability.

Re: Cyclic dependencies are evil (2013)

#102
post #18

Earlier quoted context omitted.

This is almost exactly the motivational example that Codd used when originally describing relational algebra. He described 5 different data organization schemes for a single problem and designed relational algebra to work with all of them. This ability for the same program logic to work with many different data storage layouts should make changes like you describe less painful to implement. (see page 2) E. F. Codd. 1…

My dream, when I'll have a few years of free time, is to design a language were the relational table is the primary data structure abstraction.

Mine too. My main gripe with object graphs is that often you need to make arbitrary decisions about what comes "first" -- should I represent my customers as a list of `(name, address)` records or a record `(names, addresses)` of lists? This is a silly decision to have to make, and an even sillier one to have to deal with when later you need to transpose the structure to more naturally fit some other task. Relational tables sidestep this issue.

Over the years I've found that these transposition problems are a huge drag on programs, and once you recognize them you start seeing them all over the place. They make simple and obvious relational tasks into dozens of lines of nested data structure traversal and manipulation.

The notion of transposition comes from the array programming world, where a multi-dimensional array/tensor can be seen as a tree structure, whose levels may be easily reordered by transposition. I sometimes use numpy arrays for general-purpose programming; it can be very powerful with a well-chosen representation. Unfortunately array concepts basically require the data to be rectangular -- `x[i]` has the same shape as `x[j]` -- which can be hard to adapt to general problems.

My dream then is a general-purpose data structure that takes the best of both the relational and array-programming worlds: the freedom and flatness of relational tables, enriched with tacit ideas like broadcasting that make array programming so ergonomic.

Re: Cyclic dependencies are evil (2013)

#103
post #34

Earlier quoted context omitted.

Only if you don't consider time, or optional dependencies. GCC 10 _can_ be built with GCC 10, but it was probably built with GCC 9 the first time around. If you can bootstrap it, and you can, then it's not really a hard, unbreakable cycle, right? It's just an option that's quicker than starting from tcc or whatever every time.

But how would you build the first GCC in that chain?

TCC 0.9.27 can build GCC 2.95.3. You can build TCC using GNU Mes, which itself can be built using M2-Planet, which is written in a subset of C.

https://www.gnu.org/software/mes/

Re: Cyclic dependencies are evil (2013)

#104
post #3

Language constraints aside, the real world is not something that can be cleanly modeled without the notion of circular dependencies between things. Not very many real, practical activities can be truly isolated from other closely-related activities and wrapped up in some leak-proof contract. Consider briefly the domain model of a bank. Customers rely on Accounts (I.e. have one or many). Accounts rely on Customers (i.…

I actually don't follow your example; I don't see why this is cyclic. In your example what exactly would CustomerService be responsible for that prevents it from functioning unless it has a reference to AccountService? AccountService is pretty obvious (add/remove money, link other accounts, close account, etc.) but I'm struggling to see what CustomerService would do on a per-customer basis that it would need a refere…

Funny that we both think in C++. It was what popped into my head too.

AccountMgr has many Account(s). Ditto for CustomerMgr. Interact with both to taste.

Re: Cyclic dependencies are evil (2013)

#105
post #3

Language constraints aside, the real world is not something that can be cleanly modeled without the notion of circular dependencies between things. Not very many real, practical activities can be truly isolated from other closely-related activities and wrapped up in some leak-proof contract. Consider briefly the domain model of a bank. Customers rely on Accounts (I.e. have one or many). Accounts rely on Customers (i.…

Customers and Accounts are not cyclic. A customer has an account. The customer is the domain root. Hence there would be no accounts if there were no customers. A customer does need to to hve an account. An account must have a customer. It's not cyclic in any way

Banks think the other way around - the account is important, the customer less so. Multiple entities, or no entities, might be authorized to access or control the account at various times.

Re: Cyclic dependencies are evil (2013)

#106
post #101

Earlier quoted context omitted.

My heart sinks whenever I need to spend ten minutes trying to work out which of the three different implementors is actually going to be called on a particular code path. For about a week of December, my work was solely spelunking to track down which implementations of an interface in C# were dead code and so on.

Right. Often there are 2 types of interfaces in a project. The first are "natural" interfaces, that you have put some design into and are meant to be reusable. Things like Streams or Collections. When you write a function that uses one of these, you really are expecting to be able to use any implementation. If you want to go to the implementation of Stream.Read, obviously the IDE isn't going to be able to do it, it's…

Is there any solution to this?

Re: Cyclic dependencies are evil (2013)

#107
post #34

Earlier quoted context omitted.

But how would you build the first GCC in that chain?

TCC 0.9.27 can build GCC 2.95.3. You can build TCC using GNU Mes, which itself can be built using M2-Planet, which is written in a subset of C. https://www.gnu.org/software/mes/

Furthermore, M2-Planet can be built by another C compiler written in assembly, all the way down to hex instructions.

https://github.com/oriansj/mescc-tools-seed/

Re: Cyclic dependencies are evil (2013)

#108
The mention of Lakos' book "Large-Scale C++ Software Design" reminds me of the epiphany I had when I read it. You could almost boil the entire book (and it's not a small one) down to "eliminate circular dependencies". That's trite, but Lakos walks through all the problems that large C++ projects had and lays out how all of them boil down to the dependency problem. Particularly interesting to me, at the time, was his analysis of how compile times got exponentially longer as interdependencies grew, to the point where even a small change required recompiling the entire code base.

The section in Lakos on how to identify and remove circular dependencies was worth the price of the book.

Re: Cyclic dependencies are evil (2013)

#109
post #7
post #3

Language constraints aside, the real world is not something that can be cleanly modeled without the notion of circular dependencies between things. Not very many real, practical activities can be truly isolated from other closely-related activities and wrapped up in some leak-proof contract. Consider briefly the domain model of a bank. Customers rely on Accounts (I.e. have one or many). Accounts rely on Customers (i.…

In F#, you would naturally approach this by defining a Customer independent of the Account (e.g. just containing a name and address), and an Account independent of the Customer (e.g. just containing an ID), and then a Bank which is a mapping of Account to Set . What you see as a cyclic dependency, I see as a data type that you haven't reified.

> a data type that you haven't reified

Insightful. Once you learn to see dependencies as implicitly revealing abstract data types you haven't yet teased out into existence, you start seeing all kinds of places where behavior that ought to be explicit and handled by an abstraction is smeared across multiple other concepts.

Re: Cyclic dependencies are evil (2013)

#110
post #18

Earlier quoted context omitted.

This is almost exactly the motivational example that Codd used when originally describing relational algebra. He described 5 different data organization schemes for a single problem and designed relational algebra to work with all of them. This ability for the same program logic to work with many different data storage layouts should make changes like you describe less painful to implement. (see page 2) E. F. Codd. 1…

My dream, when I'll have a few years of free time, is to design a language were the relational table is the primary data structure abstraction.

It's not quite the same, but how do you feel about Datalog or Prolog? Having a set of facts and being able to make arbitrary relations in them makes for some interesting programming. Especially if you limit your data to a more rigid normal form like RDF tuples. Datafun is also a cool upcoming language in this space https://github.com/rntz/datafun
Post reply on HN