Someone help me, a rust programmer , understand this: is this like having a function be generic over structs with some fields?
Like having `<T: {width: f64, depth: f64}>`?
I have such a hard time understanding the multiple arrows notation of ML family languages.
The only thing is I wish there was a way to safely lift a row-polymorphic record to a named record type if the compiler can determine it has all the required fields. Haven't seen any languages that offer this yet though.
What could you do with row polymorphism that you couldn’t do with generic functions that take slices in Go?
Interestingly Matlab has a specific data type for this, the cell array, as far as I am aware the only language to provide a specific data type for storing 2D arbitrarily typed data. Exactly the use case described here.
> In my personal experience I have found that when an experienced programmer who really seems like they should know better is making some weird sounding arguments about how type systems decrease their productivity and don't prevent enough bugs to be worth it, they seem to usually be complaining about the lack of row polymorphism (or the closely related structural subtyping) in popular statically typed languages, just without having the technical vocabulary for it.
Also called Nominal Typing. I generally consider it a mistake. Interfaces aren't just a bag of fields and methods. They encode semantics, too. Otherwise, you could dismiss employees with a gun or by loading them into a kiln.
I hadn't come across the "Row" polymorphism term before, but it sounds more like structural typing -- for example TypeScript, and to a lesser degree Go, have structural interfaces that provide "row polymorphic" programming.
You could go further with variant structural typing, basically this is a broader form of type checking based on call compatibility, which answers the question -- is B#foo() callable as an A#foo()?
For instance, your `area` example requires `double` result types, otherwise a row type having `width` and `length` defined as `integer` columns doesn't satisfy `area`. But, result types are naturally covariant -- `integer` is a subset of `double` -- which permits us to accept `integer` in the implementation of `area`.
Similarly, parameter types are naturally contravariant -- `Shape` is contravariant to `Triangle`, thus `B#foo(Shape)` is call-compatible as a `A#foo(Triangle)`, therefore I can pass a Triangle to `B#foo(Shape)`.
The manifold project[1] for Java is one example where the type system is enhanced with this behavior using structural interfaces.
Note, this goes further with parametric types where function result types and parameter types define variance and other constraints.
1. https://github.com/manifold-systems/manifold