-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TypeScript and Flow type definitions #55
Comments
@polytypic FYI ts-static-land (an early attempt) is superseded by https://github.com/gcanti/fp-ts which goal would be to be compatible with both fantasy-land and static-land (hopefully!) |
I made a bit of progress on TS typings in my fork. Not that I imagine typing FP libs to be easy (I ended up as the maintainer of the Ramda typings, so know some of the challenges involved, some yet unresolved), and admittedly I'm still not as fluent in this library yet either. Then again, working on typings will hopefully help there. :) |
I'm getting the impression the main challenge here is just... TypeScript's type language can't handle the reduce type logic needed to type So monocle.ts has managed to keep things typed, but essentially at the cost of sacrificing expressivity for verbosity. This makes me wonder: is there any way we could have our pie and eat it too, in any language (probably meaning Haskell/Purescript, if any at all)? I glanced over their lens libraries listed in the readme here for a bit, but I'm not so familiar with them, and haven't quite managed to tell if any enables expressive constructs as in this library while also being typed without getting a bunch more verbose. Probably a bit off-topic here, but I'd be pretty curious! |
@tycho01 the first example in the README of monocle-ts can be reduced from import { Lens, Optional } from 'monocle-ts'
const company = Lens.fromProp<Employee, 'company'>('company')
const address = Lens.fromProp<Company, 'address'>('address')
const street = Lens.fromProp<Address, 'street'>('street')
const name = Lens.fromProp<Street, 'name'>('name')
const lens = company
.compose(address)
.compose(street)
.compose(name) to something like this import { Lens, Optional } from 'monocle-ts'
const lens2 = Lens.fromPath<Employee, 'company', 'address', 'street', 'name'>(['company', 'address', 'street', 'name']) where // more overloadings here...
function fromPath<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], K4 extends keyof T[K1][K2][K3]>(path: [K1, K2, K3, K4]): Lens<T, T[K1][K2][K3][K4]>
function fromPath<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2]>(path: [K1, K2, K3]): Lens<T, T[K1][K2][K3]>
function fromPath<T, K1 extends keyof T, K2 extends keyof T[K1]>(path: [K1, K2]): Lens<T, T[K1][K2]>
function fromPath<T, K1 extends keyof T>(path: [K1]): Lens<T, T[K1]>
function fromPath(path: Array<any>) {
const lens = Lens.fromProp<any, any>(path[0])
return path.slice(1).reduce((acc, prop) => acc.compose(Lens.fromProp<any, any>(prop)), lens)
} Perhaps the same technique can be used to add typings to |
If sticking with strings as well (navigating structures of objects, no arrays), that works. When trying to extend this to numbers to navigate structures including tuples though, this breaks down. // string, ok
declare function path<T, K1 extends keyof T>(path: [K1], v: T): T[K1]
let a = path(['a'], { a: 1 }) // number
// number, fails
declare function path<T, K1 extends number>(path: [K1], v: T): T[K1] // Type 'K1' cannot be used to index type 'T'.
let b = path([0], [1]) The That said, for From a quick glance of the current library, the situation would get more complex than just string vs. number though: arrays here could contain any optic (to be created using any of the dozens of functions in this lib or just |
FWIW you can see a tuple type A = {
a: [number, [string, boolean]]
}
const lens3 = fromPath<A, 'a', '1', '0'>(['a', '1', '0'])
console.log(lens3.get({ a: [1, ['s', true]] })) // => 's' AFAIK all solutions in typed languages are based on
|
I did try that idea with inference, though that seemed to have turned out less well somehow: declare function path<T, K1 extends keyof T>(path: [K1], v: T): T[K1]
let b = path(['0'], [1])
// intended result: 1
// actual result: error
// Argument of type '["0"]' is not assignable to parameter of type '["length" | "toString" | "toLocaleString" | "push" | "pop" | "concat" | "join" | "reverse" | "shi...'.
// Type '"0"' is not assignable to type '"length" | "toString" | "toLocaleString" | "push" | "pop" | "concat" | "join" | "reverse" | "shif...'. That last example on the Monocle site looks pretty good. Just realized much of the folds from this library are also in ekmett/lens, among other things... I feel like this now. |
I guess declare function path<T, K1 extends keyof T>(path: [K1], v: T): T[K1]
let b = path(['0'], [1] as [number]) |
Cool, thanks! Trying path length 2:
Okay TypeScript, I don't think we were meant to work out... |
I made a proposal that'd enable the |
I've recently been exploring type recursion as a way to better address cases like |
@tycho01 Really interesting stuff, is there any new news about typings? |
@siassaj TS can operate on anything other than generic function types now, given |
thank you so much for the instant response. It's a shame, I've run into some typing limitations in my own work too... the really higher order stuff gets so complex so fast... I'm going to inspect your fork and see if I can't brute force some typings as needed to get most of the safety I need. |
@siassaj if you come up with something nice let us know! 🗡️ |
I have taken a look at |
Have you looked at optics-ts? It is a new optics library being developed specifically for TypeScript by @akheron. As another possibility, I recently came up with a new (to me) approach to optics while working in a project developed in C# and made a public repo in F# to demonstrate the approach: NetOptics. The significance of the approach is that it doesn't require an encoding of higher-kinded types, ad hoc polymorphism, or even the use of interfaces to manipulate (higher-kinded) abstractions and yields a relatively concise and efficient implementation. Of course, the approach has its limitations (no (higher-kinded) applicative traversal), but if one is using a language (like TypeScript) that doesn't support higher-kinded types, then the more limited and simpler typings might be a good trade-off (C# 7.3, in particular, doesn't even provide parameterized type aliases, so dealing with complex parameterized types in C# would just be incredibly cumbersome). I have no plans to make a TypeScript library using the approach, but if someone is interested in giving it a try, I can probably help to get started. |
Thank you for the reply @polytypic! I'm going to check out if |
|
Would be nice to have TypeScript and Flow type definitions for partial lenses.
The text was updated successfully, but these errors were encountered: