Type-checked keypaths in Rust
cmyr.net
cmyr.net
The program collects system resource metrics into a data structure and we need to display the fields with different styles and formats. In order to decouple the data structure from rendering, Queriable (Keyable) and FieldId (combine KeyPath + mirror struct into enum) are used. I will definitely like to checkout the KeyPath implementation as it seems more general.
// Keypath<Item, User> (from Item to User)
let item_user = \Item.user
// Keypath<User, Int> (from User to Int)
let user_age = \User.age
// Keypath<Item, Int> (from Item to Int)
let item_age = item_user.appending(user_age) auto item_user = [](auto&& item) { return item.user; };
auto user_age = [](auto&& user) { return user.age; };
auto item_age = bind(user_age, bind(item_user, _1));
But surely there must be more to keypaths than lambdas and function composition? (You do not have to use bind of course).But this touches on one thing I think would be interesting in a language. On the one hand you can define a function like you did, which is a black box that does something. On the other hand, you can talk about "the action of doing something", and assign that description to a variable, introspect it, change the action. Maybe convert from imperative to command-pattern, so you can undo, and so on. I think one of the few languages that allows something like this is Lisp. Keypaths are somewhat declarative, but don't allow introspection and are limited to getting/setting it seems.
auto item_user = [](auto&& item) -> decltype(auto) { return (item.user); };
item_user(item) = my_user;
Even more awkward if you want some slightly better error messages with concepts: auto item_user = [](auto&& item) -> decltype(auto) requires requires { item.user;} { return (item.user); };
(yes, the double requires is not a mistake).At least is very macroable, which would also allow to wrap it in a type with more meaning than a opaque lambda (which I agree is a good thing).
edit: Full on nonsense just for laughs:
auto user_age = [](auto&& user) noexcept (noexcept((user.age))) -> decltype(auto) requires requires { user.age;} { return (user.age); };For those that are interested, I found this article by FPComplete on the topic: https://www.fpcomplete.com/haskell/tutorial/lens/
Which then made its way into Apple WebObjects, Cocoa and finally Swift.
[1] https://en.wikipedia.org/wiki/Enterprise_Objects_Framework
Or just the getter part?
There is similar concept for json https://goessner.net/articles/JsonPath/
We heavily using it for validation error messages. Like
{
"error": "wrong value",
"path": "foo/bar[1]/prop"
}specified, standardized, with tests and implementations for multiple languages