Подтвердить что ты не робот

Общий тип потока для выражения функции (функции стрелок)

Обычно я стараюсь, чтобы типы функций потока были отделены от их реализации. Это немного читаемо, когда я пишу:

type Fn = string => string;
const aFn: Fn = name => `hello, ${ name }`;

а не:

const aFn = (name: string): string => `hello, ${ name }`;

При использовании общих типов мы можем написать:

const j= <T>(i: T): T => i;

const jString: string = j('apple'); // √
const jNumber: number = j(7);       // √

Но как я могу отделить этот тип от выражения функции?

type H<T> = (input: T) => T;
const h:H<*> = i => i;              // --> WHAT SHOULD GO FOR '*'?

const hString: string = h('apple'); // X error
const hNumber: number = h(7);       // X error

Что следует использовать для *? any будет работать, но это не то, что я хочу.

В haskell это не проблема:

identity :: a -> a
identity a = a

identity "a-string" // √
identity 666        // √

См. flow.org/try

4b9b3361

Ответ 1

Итак, я заметил, что если я использую bounded generics, он будет работать:

type H<T> = <T: *>(input: T) => T;
const h:H<*> = i => i;

const a: string = h('apple');      // √
const b: number = h(7);            // √
const c: {} = h({ nane: 'jon' });  // √

Не спрашивай меня, ПОЧЕМУ.

Ответ 2

 type H<T> = (input: T) => T;
    const h2:H<*> = i => i;   
    const h3:H<*> = i => i;   
    const hString: string = h3('apple');
    const hNumber: number = h2(7);

Ответ 3

Я думаю, что реальная основная проблема с вашими определениями кода и типа основана на непонимании особого свойства дженериков (также как параметрического полиморфизма): Параметричность.

Параметричность указывает, что общая функция не должна знать ничего о типах ее полиморфных аргументов/возвращаемых значений. Это тип агностик.

В результате общая функция должна обрабатывать каждое значение, связанное с полиморфным типом, независимо от конкретного типа. Конечно, это довольно ограничивает. Когда функция ничего не знает о своем аргументе, она ничего не может с ней поделать, кроме как вернуть ее без изменений.

Посмотрим на неправильную общую функцию:

const f = <a>(x :a) :a => x + "bar"; // type error

Try

f не вводит проверку как ожидалось, потому что f не должен обрабатывать x как String. Поток обеспечивает параметричность.

Обратите внимание, что дженерики гораздо полезнее в связи с функциями более высокого порядка. Ниже приведен пример правильной общей функции более высокого порядка:

type F<X, Y> = (x: X) => Y;

const apply = <X, Y>(f: F<X, Y>, x: X) :Y => f(x);

const sqr = (x :number) :number => x * x;
const toUC = (s :string) :string => s.toUpperCase();

apply(sqr, 5); // does type check
apply(toUC, "foo"); // does type check

Try

Почему вы определяете конкретную версию apply для всех возможных типов? Просто примените его к значениям произвольного типа при условии совместимости типов f и x.

Заключение

Когда у вас есть (неограниченная) общая функция, вы можете применить ее к любому желаемому значению - она ​​всегда работает так, как ожидалось. Таким образом, на самом деле нет причин объявлять отдельную функцию для каждого возможного типа. Умные люди изобрели полиморфизм, чтобы этого не произошло.

Остается проблема, т. Как только вы отделите форму определения типового типа от объявления функции, Flow больше не применяет параметричность:

const f = <a>(x :a) :a => x + "bar"; // type error

type G<a> = a => a;
const g :G<*> = x => x + ""; // type checks!?!

Try

Итак, ваш вопрос по-прежнему разумный и, к сожалению, я не могу предложить вам рецепт. Надеемся, что поведение в будущем изменится в будущих версиях.

В своем собственном ответе вы предлагаете использовать ограниченные дженерики. Я бы этого не сделал, потому что он решает проблему, которая вообще не существует, и потому, что это кажется неправильным использованием такого рода полиморфизма.