Just as the necessity of DI frameworks isn't an argument against unit testing or the inversion principle, the necessity of ORM frameworks isn't an argument against static typing or relational databases.
Just as the necessity of DI frameworks isn't an argument against unit testing or the inversion principle, the necessity of ORM frameworks isn't an argument against static typing or relational databases.
How many different types do you need to represent the result of all possible projections on a single 10 attribute relation? Now join that to another 10 attribute relation, how many types to represent all possible projections of a single join between two relations? And Maybe is not the answer, Maybe is a lie. The data is either part of the result set or it is not based on the query.
Results from joins between arbitrary queries are just lists of these types per field.
The problem is typing the agregates. So for all projections from a single table, that's 2^9 types. For all projections for a join on two 10 attribute relations, 2^19. This is in the realm of yes you can do this in a static type system, but why?
Getting back the entire relation (rather than some sub-tuple) isn't the kind of expense that causes problems scaling. It chews up network bandwidth between the database and the API, but it's not more computationally expensive (asymptotically).
And if my entities are dozens of columns wide, I'm probably not in 3NF anyway, so it'd be better to focus my efforts on getting there.
> I WISH there was a dynamic language that would statically type your attributes, that would be the perfect hybrid for me.
In statically typed database frameworks it's often the other way round: you have pseudo-dynamic typing for fields but they're static under the hood, sub-typed for the specific database type and/or stored as raw bytes.
This is basically "boxing" fields - exactly the same principle as dynamically typed languages do behind the scenes, except the library will have customised "boxing" designed for the subset of types the database supports, e.g., typed per field column rather than for every individual field in the result (like dynamic languages). This hugely reduces the overhead of actual boxing as you don't need to store the type of the field or indirect to its data.
This is sort of like dynamic typing just for the database, except the possible types are focused on a specific subset the database supports and therefore can be made efficient for the task.
Dynamically typed languages must cater for reassigning properties, fields, and/or types ad hoc, and must be made much more general - AKA slow.
For example you could (in pseudocode) do:
procedure showTitles(queryResults: QueryResults, titleName: string):
for title in queryResults.fieldData(titleName):
let str = title.getString # Returns a string type.
display str
let queryResults = db.query("SELECT * FROM MYTABLE")
showTitles(queryResults, "title")
You get the benefits of static typing and dynamic typing; type errors are caught at compile time, and you explicitly or implicitly convert the "dynamic" box for the field. For instance, trying to pass an object that isn't a `QueryResult` to the `showTitles` procedure will throw a compile time error. In dynamic languages, you could pass anything to that procedure and have to hope it doesn't blow up at run time. Testing all the possible paths and dynamic types that could be given to this procedure could be a combinational nightmare, so "to be safe" you'll have to check the type is the equivilent to a `QueryResult` anyway at run time...The `getString` function can do whatever it needs to do to convert the data if it's not stored as a string (or throw an error if this isn't possible/appropriate), whilst maintaining type relationships once it's out of the pseudo-boxed type and ensuring memory used is appropriate to the "real" type.
Source: I've written database query frameworks for statically typed languages.