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.
Cyclic dependencies are evil (2013)
51–60 of 125 posts
Re: Cyclic dependencies are evil (2013)
#52Earlier quoted context omitted.
In a relational database you would have a CustomerAccount table keeping track of which accounts that belongs to which customers. Customer would not know about Account, and vice versa. And what has reflection to do with this?
Reflection allows you to put the interfaces to Account and Customer into a shared module and the business logic implementations into their own mutually independent modules, thus removing the cyclic compile time dependency. You "wire up" the implementations at runtime, using reflection.
Re: Cyclic dependencies are evil (2013)
#53Earlier 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?
Re: Cyclic dependencies are evil (2013)
#54Language 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.
The problem is that this only lets you get customers from accounts, and not vice-versa.
And if you add a Map> to the Bank, you now have to ensure the mappings are synchronized at all times. Which, to be fair, is very straightforward as long as Bank is immutable, but still kind of annoying.
But considering how incredibly common many-to-many relationships are in business logic and especially in SQL, I'm constantly surprised that there isn't a de facto standard, efficient data structure to represent them. That speaks to how entrenched cyclic relationships are in software engineering, I suppose.
Re: Cyclic dependencies are evil (2013)
#55Earlier quoted context omitted.
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.
This resembles sql a lot. Btw I'm surprised that few languages seem to have in their standard library a many-to-many container which supports efficient lookups in either direction.
Re: Cyclic dependencies are evil (2013)
#56Language 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.…
That said, I don't see what circular dependencies have to do with reflection, much less a functional style? For a trivial example, Rust lacks reflection but the following (functional-ish) code works just fine:
mod ModA {
use crate::ModB::B;
pub struct A {
ref_to_b: Option>
}
pub fn into_b(a: A) -> Option> { a.ref_to_b }
}
mod ModB {
use crate::ModA::A;
pub struct B {
ref_to_a: Option>
}
pub fn into_a(b: B) -> Option> { b.ref_to_a }
}
Am I missing something?Re: Cyclic dependencies are evil (2013)
#57Earlier quoted context omitted.
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.
> and then a Bank which is a mapping of Account to Set The problem is that this only lets you get customers from accounts, and not vice-versa. And if you add a Map > to the Bank, you now have to ensure the mappings are synchronized at all times. Which, to be fair, is very straightforward as long as Bank is immutable, but still kind of annoying. But considering how incredibly common many-to-many relationships are in b…
Re: Cyclic dependencies are evil (2013)
#58Earlier quoted context omitted.
Reflection allows you to put the interfaces to Account and Customer into a shared module and the business logic implementations into their own mutually independent modules, thus removing the cyclic compile time dependency. You "wire up" the implementations at runtime, using reflection.
If I may, that sounds horrifying. Please, for the sake of my sanity and the IDE, always do things at compile time rather than runtime if you can!
Re: Cyclic dependencies are evil (2013)
#59Language 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 think the OP is overly dogmatic and I agree with you that sometimes you just need circular dependencies That said, I don't see what circular dependencies have to do with reflection, much less a functional style? For a trivial example, Rust lacks reflection but the following (functional-ish) code works just fine: mod ModA { use crate::ModB::B; pub struct A { ref_to_b: Option > } pub fn into_b(a: A) -> Option > { a.r…
Re: Cyclic dependencies are evil (2013)
#60Language 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.…
- The definition of Account
- The definition of Customer
- A ledger consisting of: List of Account, List of Customer, List of Account-Customer relations
A clerk works on the records on the ledger with one hand and one pen, a metaphor for the service process working on the data in the database.
An act, such as money transfer, would be described as a function.
A customer creating a new account for himself is written as `fn createAccount(Customer, NewAccountData)` because from the perspective of the clerk/bank manager/service the customer, newAccountData, and the existing data in the ledger as objects which the clerk/bank manager/service must move around in a precise way.
The module which has 'fn createAccount` depends on the types `Account` and `Customer`.
In english it roughly sounds like,
"the success of writing the rule of creating an account for a customer depends on knowing the definition of Account and Customer."
The function is not written as `customer.accountService.createAccount(newAccountData)`, because the clerk doesn't schizophrenically pretend to be the customer and create an account for himself. The clerk just receive request from the actual customer, writes new account entry and customer-account-relation entry into the ledger, that's it.
There's simply no need to call CustomerService from AccountService and vice versa. There's no need for reflections because data types are all available at compile time.