Skip to content
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

Generic type parameter problem/limitation/question #4967

Closed
myitcv opened this issue Sep 25, 2015 · 6 comments
Closed

Generic type parameter problem/limitation/question #4967

myitcv opened this issue Sep 25, 2015 · 6 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@myitcv
Copy link

myitcv commented Sep 25, 2015

Consider the following which is a modified cut-down version of the immutable-js type definition:

interface IIterable<T, K, V> {
    has(key: K): boolean;
    includes(value: V): boolean;
    slice(begin?: number, end?: number): T
    // flatMap<MK, MV>(...): Problems here!
}

interface ISeq<T, K, V> extends IIterable<T, K, V> {
    toSeq(): T;
}

interface IKeyedIterable<T, K, V> extends IIterable<T, K, V> {
    // flip(): Problems here!
}

interface List<V> extends IIterable<List<V>, number, V> {
    set(index: number, value: V): List<V>;
}

interface KeyedSeq<K, V> extends ISeq<KeyedSeq<K, V>, K, V>, IKeyedIterable<KeyedSeq<K, V>, K, V> {
}

The existence of the type parameter T for the definitions of IIterable and ISeq allows slice() to reflect that it returns a sliced instance of the same container 'type' without requiring a type assertion:

let l1: List<string>;
let l2: List<string> = l1.slice(0, 2);

However I can't type flatMap and flip. Taking flip as an example, it should return a container of the same 'type' but with K and V switched. flatMap should return a container of the same 'type' but with MK and MV.

This problem would be 'solved' if we could refer to generic types without requiring that they be passed arguments:

interface IIterable<T, K, V> {
    has(key: K): boolean;
    includes(value: V): boolean;
    slice(begin?: number, end?: number): T<K, V>
    flatMap<MK, MV>(mapper: (value?: V, key?: K, iter?: T<K, V>) => T<MK, MV>, context?: any): T<MK, MV>
}

interface ISeq<T, K, V> extends IIterable<T, K, V> {
    toSeq(): T<K, V>;
}

interface IKeyedIterable<T, K, V> extends IIterable<T, K, V> {
    flip(): T<V, K>;
}

interface List<V> extends IIterable<List, number, V> {
    set(index: number, value: V): List<V>;
}

interface KeyedSeq<K, V> extends ISeq<KeyedSeq, K, V>, IKeyedIterable<KeyedSeq, K, V> {
}

Am I missing something obvious here?

Or do I need to look to some higher order definition with code generation of (generic) interfaces to properly support this?

@mhegazy
Copy link
Contributor

mhegazy commented Sep 30, 2015

from a cursory look, it seems that flatMap should be defined as*:

export interface Iterable<K, V> {
    flatMap(
      mapper: (value?: V, key?: K, iter?: this) => this,
      context?: any
    ): this;
}

this way no need to infer any new type parameters, just pass them through, which seems to be the expected behavior of flatMap if understand the documentation correctly.

* I am refereing to new this types from #4910

@mhegazy mhegazy added the Question An issue which isn't directly actionable in code label Oct 21, 2015
@mhegazy
Copy link
Contributor

mhegazy commented Oct 21, 2015

@myitcv please reopen if this does not address the issue.

@mhegazy mhegazy closed this as completed Oct 21, 2015
@myitcv
Copy link
Author

myitcv commented Nov 4, 2015

@mhegazy apologies, somehow I totally missed your reply!

Yes #4910 does help for certain functions.

However for map and flatMap, this can't be used as the return type because those methods are themselves generic:

export interface Iterable<K, V> {
    map<M>(
      mapper: (value?: V, key?: K, iter?: this) => M,
      context?: any
    ): Iterable<K, M>; // can't use 'this' here
}

Any thoughts?

@sandersn
Copy link
Member

sandersn commented Nov 5, 2015

No, you're not missing something obvious. Typescript doesn't support higher-kinded types. Code generation might be the answer if you can't express the concept any other way.

Haskell's Functor typeclass is essentially what you want, but with more methods than just map.

The type is fmap :: Functor f => (a -> b) -> f a -> f b -- replace Functor with Iterable and you get

function fmap<F extends Iterable, A, B>(mapper: (a:A) => B, i: F<A>): F<B> {
    // code that uses the iterable interface to iterate over i...
}

Unfortunately, Typescript doesn't support higher-order types, so you'll have to find a different way of expressing this code.

@myitcv
Copy link
Author

myitcv commented Nov 5, 2015

@sandersn - thanks for confirming. Code generation is probably the cleanest solution here.

@shelby3
Copy link

shelby3 commented May 21, 2017

Unfortunately this means no Functor map nor generic iterator factories.

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

4 participants