-
-
Notifications
You must be signed in to change notification settings - Fork 95
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
reconsider TypeScript support #446
Comments
declare function chainCurriedFlipped<A>(xs: Array<A>): <B>(f: (x: A) => Array<B>) => Array<B> Results: 3 cases out of 8 require explicit type annotations const id = <A>(a: A): A => a
declare function chain<A, B>(f: (x: A) => Array<B>, xs: Array<A>): Array<B>
declare function chainFlipped<A, B>(xs: Array<A>, f: (x: A) => Array<B>): Array<B>
declare function chainCurried<A, B>(f: (x: A) => Array<B>): (xs: Array<A>) => Array<B>
declare function chainCurriedFlipped<A>(xs: Array<A>): <B>(f: (x: A) => Array<B>) => Array<B>
const xss = [[1, 2], [3, 4], [5, 6]]
const r1 = chain(id, xss) // ERROR, chain<number[], number>(id, xss) works
const r2 = chainFlipped(xss, id) // ok
const r3 = chainCurried(id)(xss) // ERROR, chainCurried<number[], number>(id)(xss) works
const r4 = chainCurriedFlipped(xss)(id) // ok
const r5 = chain(x => x, xss) // ok
const r6 = chainFlipped(xss, x => x) // ok
const r7 = chainCurried(x => x)(xss) // ERROR, chainCurried((x: number[]): number[] => x)(xss) works
const r8 = chainCurriedFlipped(xss)(x => x) // ok |
Thanks very much for the improvement, @gcanti.
Is this common in the TypeScript world? Have you spent enough time with TypeScript that you reason about when hints will be necessary, or do you assume that types will be inferred and find it frustrating when they are not? I've never worked on a TypeScript project so I'm keen to hear from you and others with relevant experience. |
I design the API specifically in order to maximize type inference, though some cases are still frustrating (the identity function above being an example) |
@davidchambers I know you used to be more enthusiastic about FlowType, where do you stand these days? The project picked up some steam in the last quarter or two. |
+1 for flow. The windows support is still trailing, but it is picking up a lot of steam. Both are great options though. With babel 7 you can use TS as a type checker like flow instead of a compiler. |
@jasonkuhrt and @defdata, I'm pleased to hear that Flow is gaining momentum. :) Can Flow describe functions such as |
@davidchambers looking at your typescript definitions looks like you did a lot of work even if you weren't entirely sure it could be done. Thank you! I work in typescript projects and I wouldn't like this to go to waste even if you decide TS/Sanctuary are not a good match. What about publishing the types in the official TS repo for now? This way it would be easier for the community to test types and even make contributions by just doing |
Just following on my last comment, I have published the types in DefinitelyTyped. The first commit was just a port of what @davidchambers had started (thanks again for that!), except I made all the changes that tslint asked me for and some rather minimalist tests. Beyond that, I've started to expand the declarations. Today I moved all methods into namespaces to be able to work with environments. The |
That's fantastic, @cortopy. Well done! I apologize for my lack of engagement in this thread. I've been busy with other Sanctuary tasks, and in recent weeks with other life tasks. I'm thrilled to see that the effort @miangraham and I spent working on TypeScript type definitions has not gone to waste. :) |
Hi there! I liked so much the philosophy and description of Sanctuary. As much as I liked it, I was disappointed by the current type-checking system. While you obviously have done great work making it, it seems it is a wrong way, unfortunately. Typescript is trending so much. Unfortunately I am not able to contribute to this great library yet, but I just wanted to give my opinion about type system: Typescript definitely should be first-class-supported, this way I think this repo could gain 10x more stars. For now I personally can not even start to work with Sanctuary because of the lack (AFAICT) of TS support. But again, your vision, @davidchambers is oh so appealing... but type checking is disappointing.. Also, I personally think type-defs are wrong way in terms of both correctness and maintainability, it is much easier to khm.. rewrite the code to typescript. BTW, Sanctuary has to deal with very complex types. If someone is interested I have a library for testing such complex types: https://github.com/qurhub/qur-typetest, helps me a lot. |
Have you tried DefinitelyTyped, @anurbol?
Once TypeScript can express |
@davidchambers have you discussed collaboration with @gcanti and @alexandru on a shared HKT lib? I believe https://github.com/gcanti/fp-ts and https://github.com/funfix/funfix share a common approach. For reference: |
@davidchambers I am sure Typescript is capable of handling the most complex scenarios possible. I apologize in advance, because I am not familiar well with haskell-like syntax except I've read "types" section of the Sanctuary docs, I am also just in the beginning of my FP journey (this is the reason why I want to choose the best FP library to start with), but if I understood correctly
type Functor<T> = (arg: T) => any
/* The following is equivalent to (a -> b) -> f a -> f b */
<A, B>( arg: (a: A) => B ) => (arg: Functor<A>) => Functor<B> BTW. Thanks to your challenge, I got familiar with Haskell syntax, and I must say it is much more concise and elegant... i.e. I found out that I don't really know what argument names like |
@anurbol I think you have a little too much faith in Typescript. I use TS professionally at my job, and it lacks a lot of features that would make functional programming feel natural. One of these is Higher Kinded Types, which TS does not support directly, although there are some work arounds (see below): microsoft/TypeScript#1213 Haskell's type system is strictly more power than TS. You can represent any TS construct in Haskell's type system, but the opposite is not true. What you have above assumes that Functor is a type, whereas it is in fact a type class, more similar to an interface than a class or a type. This means that if you have three generic types:
We need to be able to represent that (and even more complicated notions) in the type system, without defaulting to using I would suggest playing around with implementing it yourself, and then taking a look here: https://github.com/gcanti/fp-ts to see one way it's been tackled. I would also suggest taking a look at the types @davidchambers posted above. You can see that the developer simply punted on the issue of Higher Kinded types, leaving the actual type checking to Sanctuary. In the past few months I have run into numerous issues in typescript trying to do things that would be trivial with Sanctuary. In addition, there are many errors that it won't help you catch at all. For instance, exceptions aren't represented in the type system, so it's not possible to know if a particular function can blow up your program. In addition, the fact that Sanctuary checks types at runtime gives it even more power. I have tried to use decorators to modify the type of a method at runtime. It's impossible in Typescript because TS only has compile time information. Sanctuary doesn't have this limitation. If you're truly interested in using functional programming in JS, I don't think Typescript is in any way ideal. I would suggest using regular JS along with libraries like Sanctuary or Ramda, or else go whole hog and check out a language like Elm or Purescript which have been built from the ground up to facilitate strongly typed functional programming that compiles to Javascript. |
@anurbol A simple definition of Functor in pseudo-TypeScript would be: type Functor<F> = { map: <A, B>(f: (a: A) => B) => (fa: F<A>) => F<B> } In other words, a If you're interested in good TypeScript support for functional programming libraries (something I think many users are very interested in!) you should find the relevant issues in the TypeScript issue tracker and provide your feedback. |
@pelotom recently put https://github.com/pelotom/hkts up as well. I haven't used it, but it looks very concise. |
I want to apologize once again, I've looked how much work was done on sanctuary's type system, and it looks like a piece of art. |
Thanks for your words of encouragement, @anurbol. 😊 |
I just want to make sure that Sanctuary’s authors are aware of the recent typescript 3.4 release notes that include “higher order type inference from generic functions.” Sanctuary looks wonderful. The only thing holding me back is the lack of typescript support. |
Thank you, @RichardForrester. It seems we should have another go at writing type definitions. :) |
This library really hits a sweet spot for me -- as FP as you can get without ditching the NPM eco-system. I'm vegging out today writing a bunch of I also installed This is obviously just my opinion, the perspective of a ts user who is strongly considering adopting Sanctuary.js as my main daily driver, but I think as far as Typescript support goes, I honestly don't care about having super fantastic typings because it's probably still impossible to even have good ones and that's fairly common with even some great libraries that put a lot of time into trying to get typings perfect. My ramda code is riddled with However, basic typescript support, to allow for auto import and some IntelliSense with VSCode goes a long way. The truth for me, and probably a lot (if not most) people using Typescript, is that I use it for the little conveniences, especially with VSCode. I'm talking about renaming functions across your project with a single command, moving files and having all of the related imports automatically updated, auto import when typing some function name. Once you get used to those things, you can't go back. But the actual typings are kind of pain... they are wrong more than my code is, meaning that a lot of debugging time turns out spent on fixing problems with typings or finding escape hatches for bad typings and not with the javascript. Still, when it comes to refactoring, I will never go back to plain javascript. I think you are dead right when you say that you worry that the implementation of Sanctuary.js should not be affected by trying to satisfy the limitations of Typescript. You should stay true to that and if it makes sense to you to include some basic type definitions for enabling those free wins you get with TS it should be okay. But if you have to choose between spending ?? hours writing and maintaining near perfect typings that will never be exactly what you want them to be, and improving the actual library, I'd say the |
@RichardForrester thank you mentioning that the types are working well - I've been on the fence about them because the first line is
(and it looks like Sanctuary's at v2 now - wow! Congrats!) But perhaps it's worth waiting for the hypothetical day TypeScript implements something that lets pipes type properly without using casts and assertions. That might be a big ask of the TS team though, since it's a non-trivial problem. (There's also Gcanti's fp-ts and io-ts which can do some of the things Sanctuary does like Maybe/Option types, and type checking arbitrary objects (~sanctuary-def) that might be a more burning need for TS programmers in the interim) |
I know this thread is old but I want to chime in with how I just got IntelliSense support in vscode for Sanctuary. I'm developing a js project using vscode and First I installed @types/sanctuary via {
"compilerOptions": {
"checkJs": true
},
"exclude": ["node_modules", "**/node_modules/*"]
} In my index.htm I got: <script src="https://cdn.jsdelivr.net/gh/sanctuary-js/sanctuary@3.1.0/dist/bundle.js"></script>
<script type="module" src="js/app.js"></script> and in js/app.js I got: /** @type {import('sanctuary')} */
const S = window.sanctuary
const $ = window.sanctuaryDef Now I get code completion on I fixed this by creating a globals.d.ts in a subfolder, but according to vscode documentation it can be anywhere in your project. In globals.d.ts I have: interface Window {
sanctuary: import('sanctuary');
sanctuaryDef: import('sanctuary-def');
} The That is as far as I got. For But this is way better than nothing! And hopefully helpful for someone else :) 1: Unfortunately there is no default file set, which seems to be a prerequisite for sha256 integrity on jsDelivr. |
I got some help by @jcalz at https://stackoverflow.com/q/66817628/205696, to get intellisense support in vscode for sanctuary-def and sanctuary-type-identifiers. Now my global.d.ts looks like this: import * as S from 'sanctuary'
import sanctuaryDef from 'sanctuary-def'
import sanctuaryTypeIdentifiers from 'sanctuary-type-identifiers'
declare global {
interface Window {
sanctuary: typeof S;
sanctuaryDef: typeof sanctuaryDef;
sanctuaryTypeIdentifiers: typeof sanctuaryTypeIdentifiers;
}
} That means I do not have to define the type for Sanctuary with "devDependencies": {
"@types/sanctuary": "^3.0.3",
"sanctuary-def": "^0.22.0",
"sanctuary-type-identifiers": "^3.0.0"
} |
Thank you for sharing your workaround, @dotnetCarpenter. |
Update (2017-10-03): I have updated
chainCurriedFlipped
to delay the introduction ofB
, as suggested by @gcanti. I have also updated the results to correct an unrelated error I made initially (providing arguments tochainCurriedFlipped
in the wrong order).@miangraham and I have been working on #431 for several weeks. We've made good progress despite the impedance mismatch between the two type systems.
I had trouble defining types for
chain
. To simplify the task I specialized the type signature:I couldn't get this to work. I asked for help in the TypeScript room on Gitter and @sluukkonen provided helpful responses (including a link to microsoft/TypeScript#17520). I learnt that certain combinations of removing currying, changing the argument order, and replacing
id
withx => x
(!) would appease the compiler.I defined four versions of “chain” to discover the magic combination:
Results of applying functions to
id
andxss
chain
<A>(x: A) => A
is not assignable to parameter of type(x: {}) => {}[]
.chainFlipped
[1, 2, 3, 4, 5, 6]
chainCurried
<A>(x: A) => A
is not assignable to parameter of type(x: {}) => {}[]
.chainCurriedFlipped
[1, 2, 3, 4, 5, 6]
Results of applying functions to
xs => xs
andxss
chain
[1, 2, 3, 4, 5, 6]
chainFlipped
[1, 2, 3, 4, 5, 6]
chainCurried
(xs: {}) => {}
is not assignable to parameter of type(x: {}) => {}[]
.chainCurriedFlipped
[1, 2, 3, 4, 5, 6]
My reactions to the tables above:
id
andxs => xs
are not equivalentI'm concerned that even if we succeed in approximating type classes with interfaces, complications such as these will make Sanctuary and TypeScript an awkward couple. What do others think?
Sanctuary was designed as a JavaScript library. Had we set out to create a functional programming library for TypeScript it's likely we would have done things quite differently. I'm concerned that treating both JavaScript and TypeScript as first-class targets will lead to compromises. I'd like to continue to focus on making Sanctuary a principled, capable JavaScript library. Perhaps the TypeScript type definitions should live elsewhere so TypeScript's limitations won't influence the design of our API.
The text was updated successfully, but these errors were encountered: