For example, ActiveRecord doesn't have support for window functions, but its underlying AST (a library called ARel) allows you to extend it, which at one point I did, allowing me to write something like this:
things = Arel::Table.new('things')
things.project(Arel::Nodes::NamedFunction.new(:sum, [things.count]).over('my_window')).
window('my_window').
order(things[:name]).
range(Arel::Nodes::Preceding.new)
which generates something like: select sum(count(*)) over my_window
from things
window my_window as (order by name rows unbounded preceding)
(It's been a while since I looked at this particular code, which is no longer in production use, so it might not be correct.)In other words, I could continue to express whatever I wanted of SQL, at the language level — no type mismatches.
The SQL builder code isn't pretty, to be sure, but it's something you typically encapsulate into a lower-level module rather than writing in application code. And as it's highly analogous to the SQL code it generates, it's not like it's that much more complicated. (ARel isn't a shining example of how to do it right, either. I expect Diesel implements the AST aspect more cleanly.)
Edit: I see what you're saying about typed record types. I was thinking more about the grammar, not the shape of the data.