Comments (20)
Any update regarding this feature? There was no response within 30 days yet this feature request is not yet closed. I'd also like to see this become reality.
from flow.
Here's a (hacky) way to implement HKT: https://github.com/gcanti/flow-static-land
from flow.
Thanks, @gcanti -- that seems like a reasonable workaround for now.
Just to follow up with something semi-official: There's a chance we may get to this at some point, but it's not on the near-term horizon for the core team. As we've done so far with this, we'll leave this issue open to track the request for if/when this surfaces the priority queue.
from flow.
@astampoulis this is @hallettj's encoding I'm currently using in fp-ts
type Either<L, R> = { type: 'Left', value: L } | { type: 'Right', value: R }
type EitherF = <L, R>(x: [L, R]) => Either<L, R>
type ArrayF = <A>(x: [A]) => Array<A>
type ArrOfStrings = $Call<ArrayF, [string]>
type EitherOfStringNumber = $Call<EitherF, [string, number]>
;([1]: ArrOfStrings) // number. This type is incompatible with string
;({ type: 'Right', value: 'foo' }: EitherOfStringNumber) // string. This type is incompatible with number
from flow.
It's necessary for defining monads (a special kind of mappable):
interface Monad<A, M<>> {
static map<B>(f:(a:A) => B): M<A> => M<B>;
static lift(a:A): M<A>;
static join(mma: M<M<A>>): M<A>;
}
Basically any collection type (even an asynchronous collection like a stream) implements the monad interface; so do parsers, promises, membranes, encapsulated state (particularly useful for serialized computations), and many more design patterns.
Here's code for how one could implement the standard list monad. This particular monad is clunky because it's already built into javascript with special syntax, but the other features above turn out particularly nicely using this interface.
class ArrayMonad<A> implements Monad<A, Array> {
static map<B>(f:(a:A) => B): Array<A> => Array<B> {
return function (arr: Array<A>): Array<B> {
return arr.map(f);
};
}
static lift(a:A): Array<A> { return [a]; }
static join(mma: Array<Array<A>>): Array<A> {
var result: Array<A> = [];
var len = mma.length;
for (var i = 0; i < len; ++i) {
result = result.concat(mma[i]);
}
}
}
ArrayMonad.map((x)=>''+x+x)([1,2,3,4]); // ['11','22','33','44']
ArrayMonad.lift(5); // [5]
ArrayMonad.join([[1,2,3],[4,5],[],[6]]); // [1,2,3,4,5,6]
Another example:
interface Cartesian<T, F<>> {
function all(arr: Array<F<T>>): F<Array<T>>;
}
class PromiseCartesian<T> implements Cartesian<T, Promise> {
function all(arr: Array<Promise<T>>): Promise<Array<T>> {
// return a promise that resolves once all the promises in arr have resolved
}
}
class MaybeCartesian<T> implements Cartesian<T, ?> {
// If all the elements of arr are non-null, returns arr; otherwise returns null.
function all(arr: Array<?T>): ?Array<T> {
var len = arr.length;
var result:Array<T> = [];
for (var i = 0; i < len; ++i) {
if (arr[i] == null) { return null; }
result.push(arr[i]);
}
return result;
}
}
from flow.
Is there still no way to do this "officially"?
from flow.
@jeffmo In the year+ since your last comment, how has thinking on this by the core team changed if at all?
from flow.
From what I can tell, higher-kinded types are actually supported in Flow, though they are encoded using the simple-kinded function type and the utility type $Call
for type-level application. In fact, some of the utility types of Flow are using this encoding, such as the $ObjMap<T, F>
type, where F
is higher-kinded. Maybe this isn't official though, because I don't see it explicitly mentioned in the documentation, or I might be missing something.
The encoding is as follows:
the kind Type -> Type
is encoded as the type Function
the type-level function Ξ»A.F(A)
is encoded as the type <A>(A) => F(...A...)
the type-level application F T
is encoded as the type $Call<F, T>
the conversion-rule is not fully supported, so some times you have to explicitly use an intermediate cast to Actually this seems to work right.any
to view a variable of type $Call<F, T>
as having the type of the Ξ²-redex F[T]
, so for example if we have type F = <A>(A) => Array<A>
, a variable of type $Call<F, string>
is not immediately typeable as Array<string>
. The conversion rule is used for expressions though, so that part is type-safe.
Not sure if there is a way to encode kinds like (Type -> Type) -> Type
(e.g. for monads), maybe by using a more restricted type than Function
to encode the higher kinds.
from flow.
Any updates here? It's so hard to use Flow for functional programming without this. :(
from flow.
π
from flow.
Interesting. We have some ideas on how we can generalize type aliases to model higher kinds in the future; but I'm curious if you already have some concrete examples that you need this feature to type check.
from flow.
the mappable interface is pretty much the kind of interface that I'd like to use.
simple example:
interface List<A> extends Mappable<List<A>> {
....
// so we have map<B>(func: (a:A) => B): List<B>;
}
interface Sequence<A> extends Mappable<Sequence<A>> {
....
// so we have map<B>(func: (a:A) => B): Sequence<B>;
}
function map<M,A,B>(mappable: Mappable<M<A>>, func: (a:A) => B): M<B> {
return mappable.map(func)
}
function mapLenghth<M>(mappable: Mappable<M<{length : number}>>): M<number> {
return mappable.map(mappable, a => a.length);
}
var list: List<string>;
var lengthList = mapLength(list); // List<number>
var sequence: Sequence<string>;
var lengthSeq = mapLength(sequence); // Sequence<number>
var array: Array<string>;
var lengthArray = mapLength(array); // Array<number> since in fact
// Array<A> implements Mappable<Array<A>>
//etc...
Note that in this example we always have mappable mapping to their own type, but we could have for example :
class Sequence<A> implements Mappable<Array<A>> {
map<B>(func: (a:A) => B): Array<B> {
return Array.from(...this, func);
}
}
from flow.
Here's another code dump that aligns more closely with the syntax we have now.
type Monoid<T> = {
zero: T;
append: (a: T, b: T) => T;
}
// These are fine
var sumMonoid: Monoid<number> = {
zero: 0,
append: (a, b) => a + b
}
var productMonoid: Monoid<number> = {
zero: 1,
append: (a, b) => a * b
}
// The type variable T can't be free, but this
// formulation is valid for any type T.
var arrayMonoid: Monoid<Array<T>> = {
zero: [],
append: (a, b) => a.concat(b)
}
// We should be able to use any type, not just
// built-ins.
type Transformation<T> = (x: T) => T;
var transformationMonoid: Monoid<Transformation<T>> = {
zero: (x) => x,
append: (f, g) => (x) => f(g(x))
}
// HKT use case
function concat<T>(array: Array<T>, monoid: Monoid<T>): T {
return array.reduce(monoid.append, monoid.zero);
}
var ex1: number = concat([1, 2, 3], sumMonoid);
var ex2: number = concat([1, 2, 3], productMonoid);
var ex3: Array<number> = concat([[1, 2], [3, 4]], arrayMonoid);
var ex4: Transformation<number> = concat([(x) => x + 1, (x) => x - 1], transformationMonoid);
Note that these "type classes" are a bit more cumbersome to use than they would be in Haskell, where the compiler will thread the dictionaries around for you, or Scala where the implicit rules will do the same.
from flow.
Oddball edge case. Given I
:
interface I {
method(): string;
static sMethod(): string;
}
should the following error out?
class A implements I {
method: void => string;
static sMethod: void => string;
constructor() {
this.method = function () { return 5; } // NG
}
}
A.sMethod = function () { return "peep"; }
from flow.
@popham I'm not sure how that is related to HKT. Can you elaborate?
from flow.
Off-topic. Apologies. (I'm imagineering what interface
should look like, and this is the only lead I could find. I just went to delete the prior, but then I saw your response.)
from flow.
Looking for this feature as well.
from flow.
Higher order types should also include recursive types. This would help in cases like Lodash's flatten
.
type Flattenable<A> = Array<A> | Array<Flattenable<A>>;
declare function flatten<T>(list: Flattenable<T>): Array<T>;
from flow.
@popham Your example isn't really related to HKT either. Also, recursive types should work. I think your type definition is wrong. You probably want something like this:
type Flattenable<A> = Array<A | Flattenable<A>>;
var foo: Flattenable<string> = ['a', ['a'], [['a']]]; // OK
from flow.
Also, for what it's worth. Using the existential types that are on master right now, my previous example with 2 character substitutions type checks:
/* @flow */
type Monoid<T> = {
zero: T;
append: (a: T, b: T) => T;
}
// These are fine
var sumMonoid: Monoid<number> = {
zero: 0,
append: (a, b) => a + b
}
var productMonoid: Monoid<number> = {
zero: 1,
append: (a, b) => a * b
}
var arrayMonoid: Monoid<Array<*>> = {
zero: [],
append: (a, b) => a.concat(b)
}
type Transformation<T> = (x: T) => T;
var transformationMonoid: Monoid<Transformation<*>> = {
zero: (x) => x,
append: (f, g) => (x) => f(g(x))
}
// HKT use case (edit: not actually HKT)
function concat<T>(array: Array<T>, monoid: Monoid<T>): T {
return array.reduce(monoid.append, monoid.zero);
}
var ex1: number = concat([1, 2, 3], sumMonoid);
var ex2: number = concat([1, 2, 3], productMonoid);
var ex3: Array<number> = concat([[1, 2], [3, 4]], arrayMonoid);
var ex4: Transformation<number> = concat([(x) => x + 1, (x) => x - 1], transformationMonoid);
from flow.
Related Issues (20)
- Does not narrow down type after undefined/falsy check HOT 1
- Recursive/deep Partial HOT 2
- Bugs in negated `instanceof` expressions HOT 1
- [Question]: How to fix flowgen test after flow update HOT 3
- [Discussion]: Any plans to make Flow more interpopble with TS ecosystem? HOT 5
- Higher order type parameter support HOT 1
- Readme links to 404
- Strange index signature error if you use $Keys<O> instead of the equivalent literal union inside $ReadOnlyArray HOT 1
- [homebrew] automation script needs update HOT 2
- "Unxepcted identifier, expected the token ;" when using await inside of ES module HOT 3
- Can't iterate through chars in a string HOT 4
- Flow does not catch invalid switch statements HOT 1
- Conditional type only works if I inline it HOT 5
- Object with null prototype does not extend interface {} HOT 3
- Missing documentation for "render-types"? HOT 2
- Relay + graphql literals HOT 11
- [homebrew] regression test failure with 0.226.0,0.227.0 HOT 2
- FLOW BUG: This is a misplaced error HOT 3
- The `experimental.ts_syntax` doesnβt seem to do anything HOT 2
- Parameters<> does not support optional params HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from flow.