5 ms·
Typed Keypaths are one of the Swift language features which I really miss in any other language. They're incredibly powerful because it is possible to compose t
by terhechte 4y ago
Typed Keypaths are one of the Swift language features which I really miss in any other language. They're incredibly powerful because it is possible to compose them. Imagine a struct or class `Item` with a `user` field and the `User` has an `age` of `Int`:
// 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)
- hither_shores 4y agoMost languages with good support for functional programming have lenses, which generalize keypaths. Off the top of my head: Haskell, Purescript, TypeScript, Scala, OCaml, Racket, and Common Lisp all have lens libraries.
- wyager 4y agoFor (typed) optics to work well you need HKTs and universal quantification over those types, which rules out many of the languages you listed. Haskell and PureScript should be fine. I don't see how it would work in TS, scala, ocaml. Lisp should work, but no types right? No idea about racket.
- hither_shores 4y agoFor general optics, yes. But lenses and to a lesser extent prisms still work ok in their naive getter/setter and constructor/pattern encodings.
- shirogane86x 4y agoscala has HKTs and should be able to implement lenses just fine, I think. TS and OCaml can emulate HKTs in a really awkward way, but they can also just implement a less general more restricted subset.
- jasonhansel 4y agoIMHO the issue is that lenses are too powerful. For this reason, they are liable to be used in overly clever ways that reduce readability and increase complexity. Things like keypaths are useful because they are (intentionally) more restricted than lenses, and are thus less likely to be misused.
- gpderetta 4y agoIn c++: 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).
- captainmuon 4y agoI think keypaths also permit writing (but maybe the rvalue-reference does that?). And probably they allow using properties and not just fields, but I don't know Swift tbh. 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.
- gpderetta 4y agoDuring return type inference, references decay to value types by default, so you have to use some awkward syntax if you want to use them to set values: 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); };