It would be useful for library definitions to have a new utility type $Not<A>, to create a type that accepts any type except type A.
For example in flow-typed ramda definition we have something like:
declare type $npm$ramda$Placeholder = { "@@functional/placeholder": true, ... };
declare type __CurriedFunction1<A, R, AA: A> = (...r: [AA]) => R;
declare type CurriedFunction1<A, R> = __CurriedFunction1<A, R, *>;
declare type __CurriedFunction2<A, B, R, AA: A, BB: B> = (
((...r: [AA]) => CurriedFunction1<BB, R>) &
((...r: [$npm$ramda$Placeholder, BB]) => CurriedFunction1<AA, R>) &
((...r: [AA, BB]) => R)
);
declare type CurriedFunction2<A, B, R> = __CurriedFunction2<A, B, R, *, *>;
This cause FlowJs to hit recursion limit more often , I guess we could help flowJs with $Not utility type such as:
declare type __CurriedFunction2<
A: $Not<$npm$ramda$Placeholder>,
B: $Not<$npm$ramda$Placeholder>,
R,
AA: A,
BB: B
> = (
((...r: [AA]) => CurriedFunction1<BB, R>) &
((...r: [$npm$ramda$Placeholder, BB]) => CurriedFunction1<AA, R>) &
((...r: [AA, BB]) => R)
);
I'd also like to implement this feature, but I'm having serious trouble to understand the codebase. But I'm decided to give it a try
@moroine what's trouble?
I'll try to implement, if you still want to dive into codebase just ask me in Flow discord
@moroine

Can you make full example, so I can test if it works for your case?
That's amazing!
I've created a test case below.
https://flow.org/try/#0CYUwxgNghgTiAEAXAngBwQEgHaoLYZil2CgwAVowQALAewlBngF54BveAIgAFuAzAK5YwiAJa0sUCAHpUlGvUacAXEhgCQAGngA6PfAC+AbgBQJ0JFgJBwsRPi4QMAOYgAPAEFtAIQB8ACi94bwBKVQ49HQwAUQAPKBFPX21ImPjEv0NTC2g4eBsRcSwHJ1dPHwCPMOCWX3ZdPTSExCSUxrjmt0zjE2lpeByrfKFC+0cXd17++HgPVQwAORBnKER3bDwCIhJyeToGJ2SpmeD5pZW1tw38QmJSCgSFA5hfY8rq71r61I7Ej2SGlFfi1utlwLlrCM7MVxmUgn5-Nctnddo99owfNUPF8Iu10i1-m0gfiunUeiYwBIAM6IeAAfTp8xwN229z2iicLHqPH4UKKUlk7OeKjUGiyZkpWBp8BgAEZwvAoKosAJcAAjJzaNWqGkwURYZyGLmwkD+DhK+CywzaDja+AAcjV9sMIVMJv8DJtitUVoMITN8DtjudftMZjMfWCAlpokQ9qp8AA7rQYABrfWGxOx6iK4DAczgoYFaG54D+FXqzXwCsamDVGtOMGWPLFoql8uq2v1zuc5h1BswJsQ4a2NtQPOI5nInYPKjoqsD7uVph96s9wdmcdlz2W-0AJldQA
Doesn't work :\
Did you push your work somewhere, I'd like to have a look and maybe trying to help
No, wait, I'll push
I'd like to be able to use $Negated to model n-ary operations that take at least 2 operands, e.g.
type AtLeastTwo<T> = T[] & $Negated<[] | [T]>;
type Expr = {
kind: "add",
args: AtLeastTwo<number>,
} | {
kind: "mul",
args: AtLeastTwo<number>,
} | ...
({kind: "add", args: []}: Expr); // error
({kind: "add", args: [1]}: Expr); // error
({kind: "add", args: [1, 2]}: Expr); // okay
@goodmind would what I wrote work with the $Negated you implemented?
It's not easy to jump into Ocaml... I've read the Ocaml doc, but this codebase is too complex for me 馃槥
I'll try to follow & understand, but there is no way I can be useful on this one except writing documentation.
@moroine did you manage to build it?
you can learn ReasonML first, then OCaml
@kevinbarabash I don't think it does, maybe quick_subtype doesn't support tuples

Nah, quick_subtype is useless, doesn't work with most types
That's too bad. Thanks for checking.
Instead of adding $Not to Flow (which I'm not convinced is necessary nor desirable), let's take a closer look at the libdef to see if it can be simplified.
declare type $npm$ramda$Placeholder = { "@@functional/placeholder": true, ... };
declare type __CurriedFunction1<A, R, AA: A> = (...r: [AA]) => R;
declare type CurriedFunction1<A, R> = __CurriedFunction1<A, R, *>;
declare type __CurriedFunction2<A, B, R, AA: A, BB: B> = (
((...r: [AA]) => CurriedFunction1<BB, R>) &
((...r: [$npm$ramda$Placeholder, BB]) => CurriedFunction1<AA, R>) &
((...r: [AA, BB]) => R)
);
declare type CurriedFunction2<A, B, R> = __CurriedFunction2<A, B, R, *, *>;
A few things don't make sense to me here:
A in it's type. Why is the type param necessary? Same with A and B in _CurriedFunction2* is deprecated but used everywhere. This could also be contributing to the stack overflows (which is very deep, btw. you have to make Flow recurse 10k times into a single type for that to happen, which means it might actually be in an infinite loop). I don't think this libdef would need * at all if it cut down on the amount of type params it has. ((...r: [$npm$ramda$Placeholder, BB]) => CurriedFunction1<AA, R>) &
((...r: [AA]) => CurriedFunction1<BB, R>) &
((...r: [AA, BB]) => R)
Somewhat related, but I would love to be able to specify a non-callable object type. As far as I can tell that is currently impossible with Flow. Also related, but being able to specify object types that don't have certain properties with some kind of sigil, perhaps ! prefixing the key would also be great:
type FooBar = {| foo: string, bar: number |};
type JustBar = {| ...FooBar, !foo: any |}; // equivalent to {| bar: number |}
type NonCallableObject = { ![[call]]: any };
@jbrown215 thanks for taking the time to have a look at the ticket
- _CurriedFunction1 doesn't use
Ain it's type. Why is the type param necessary? Same with A and B in _CurriedFunction2
After some investigations, I found out the reason. A lot of ramda function are defined using var instead of function which errors when using your proposal. Try flow
This will make impossible to use CurriedFunction helper, as it needs to be done at each level. Do you have any suggestion on how to type this?
2.
*is deprecated but used everywhere. This could also be contributing to the stack overflows (which is very deep, btw. you have to make Flow recurse 10k times into a single type for that to happen, which means it might actually be in an infinite loop). I don't think this libdef would need*at all if it cut down on the amount of type params it has.
馃憤
3. Why do you need $Not? Couldn't you just put the rambda placeholder first in the overload?
((...r: [$npm$ramda$Placeholder, BB]) => CurriedFunction1<AA, R>) & ((...r: [AA]) => CurriedFunction1<BB, R>) & ((...r: [AA, BB]) => R)
It indeed fixes the error, but I feel it unnatural that the order matter that much. & is not supposed to be commutative?
After some investigations, I found out the reason. A lot of ramda function are defined using var instead of function which errors when using your proposal.
Are all of the rest arguments necessary? They have tuple types, so the arity of the function is known statically.
Everything works as expected when I get rid of them.
& is not supposed to be commutative?
We use the order of types in function intersections as an "overloaded" function. We try each case in the intersection one by one until we find a match and we use that function.
Are all of the rest arguments necessary? They have tuple types, so the arity of the function is known statically.
I think that's a way to define different functions depending on the number of parameters. As the returned type will be completely different. I don't think there is a way to make the number of parameters exact in a function declaration
I think that's a way to define different functions depending on the number of parameters. As the returned type will be completely different.
But the rest parameter is constrained by a tuple, which means that you can't have more arguments than there are elements in the tuple. Right?
Yeah, tuple is used to discriminate branches from the number of arguments. Without rest, we have errors with currying such as Try
@jbrown215 I think one of the issues is that function is that we need kindof "exact function", i.e. having exact number of parameters.
When I use the following definition, it seems to work properly Try
declare type CurriedFunction1<A, R> = (A, ...noRest: []) => R;
declare type CurriedFunction2<A, B, R> = (
(($npm$ramda$Placeholder, B, ...noRest: []) => CurriedFunction1<A, R>) &
((A, ...noRest: []) => CurriedFunction1<B, R>) &
((A, B, ...noRest: []) => R)
);
After digging in, it was indeed related to function overloading. But I'd prefer to use rest params as being typed as empty tuple as it's easier to maintain