GithubHelp home page GithubHelp logo

Support higher kinded type about flow HOT 20 OPEN

facebook avatar facebook commented on April 20, 2024 71
Support higher kinded type

from flow.

Comments (20)

pmlt avatar pmlt commented on April 20, 2024 19

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.

gcanti avatar gcanti commented on April 20, 2024 13

Here's a (hacky) way to implement HKT: https://github.com/gcanti/flow-static-land

from flow.

jeffmo avatar jeffmo commented on April 20, 2024 6

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.

gcanti avatar gcanti commented on April 20, 2024 6

@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.

metaweta avatar metaweta commented on April 20, 2024 5

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.

Jacse avatar Jacse commented on April 20, 2024 4

Is there still no way to do this "officially"?

from flow.

jasonkuhrt avatar jasonkuhrt commented on April 20, 2024 4

@jeffmo In the year+ since your last comment, how has thinking on this by the core team changed if at all?

from flow.

astampoulis avatar astampoulis commented on April 20, 2024 3

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 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. Actually this seems to work right.

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.

PinkaminaDianePie avatar PinkaminaDianePie commented on April 20, 2024 1

Any updates here? It's so hard to use Flow for functional programming without this. :(

from flow.

wizzard0 avatar wizzard0 commented on April 20, 2024

πŸ‘

from flow.

avikchaudhuri avatar avikchaudhuri commented on April 20, 2024

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.

fdecampredon avatar fdecampredon commented on April 20, 2024

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.

samwgoldman avatar samwgoldman commented on April 20, 2024

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.

popham avatar popham commented on April 20, 2024

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.

samwgoldman avatar samwgoldman commented on April 20, 2024

@popham I'm not sure how that is related to HKT. Can you elaborate?

from flow.

popham avatar popham commented on April 20, 2024

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.

rockymadden avatar rockymadden commented on April 20, 2024

Looking for this feature as well.

from flow.

dead-claudia avatar dead-claudia commented on April 20, 2024

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.

samwgoldman avatar samwgoldman commented on April 20, 2024

@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.

samwgoldman avatar samwgoldman commented on April 20, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.