2017-02-19 8 views
3

Попытка получить мои ноги влажными, используя TypeScript, и я продолжаю работать в trouble. old function resurfaced today и как упражнение, мне было любопытно, могу ли я преобразовать его в TypeScript. Пока это была полная боль в шее.Должен ли я указывать имена параметров для типов функций более высокого порядка в TypeScript?

declare type Ord = number | string; 

// type signature for f sucks really bad 
// (f: Ord => Ord => boolean) would be really nice, if possible 
// but instead I have to give names (_) for the parameters? dumb 
const arrayCompare = (f: (_: Ord) => (_: Ord) => boolean) => ([x,...xs]: Ord[]) => ([y,...ys]: Ord[]): boolean => { 
    if (x === undefined && y === undefined) 
    return true; 
    else if (! f (x) (y)) 
    return false; 
    else 
    return arrayCompare (f) (xs) (ys); 
} 

// here the names of the parameters are actually used 
const eq = (x: Ord) => (y: Ord) : boolean => x === y; 

// well at least it works, I guess ... 
console.log(arrayCompare (eq) ([1,2,3]) ([1,2,3]));    // true 
console.log(arrayCompare (eq) (['a','b','c']) (['a','b','c'])); // true 

Поэтому вопрос конкретно о (см смелое)

const arrayCompare = (f: (_: Ord) => (_: Ord) => boolean) => ...

f ожидает функции высшего порядка типа

Ord => Ord => boolean 

Но если я использую этот тип Подпись

// danger !! unnamed parameters 
(f: (Ord) => (Ord) => boolean) 

машинопись Предположит Ord как имени параметра и подразумеваемого тип any

// what TypeScript thinks it means 
(f: (Ord: any) => (Ord: any) => boolean) 

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

// now it's correct 
(f: (_: Ord) => (_: Ord) => boolean) 

Но c'mon, что не имеет никакого смысла. У меня есть только доступ к f в этом контексте, а не к параметрам, которые f будут связываться, когда я в конце концов называть его ...

Вопрос

Почему я должен предоставить имена для более высокого порядка функции в TypeScript?

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


UPDATE

«, насколько это имена параметров, рассмотрим функцию, которая принимает дозвона -> (номер -> номер -> номер) ->, таким образом, исходя исключительно по типам, которые вы выбрали: добавление, вычитание, умножение, деление, мощность, сравнение которых только один имеет смысл, теперь, если параметр обратного вызова имел имя add: (number -> number -> number), выбор был бы очевиден " - Aleksey Bykov

Я счастлив, что получил возможность ответить на это. Я могу назвать кучи больше функций с подписью (number -> number -> number).

  • first, second, mod, min, max
  • битовые функции &, |, xor, << и >>
  • (x, y) => sqrt(sq(x) + sq(y))
  • (x, y) => x + x + y + y + superglobalwhocares
  • и любой другие функции вы с сон вверх

Чтобы прояснить ситуацию, я не предлагаю, чтобы сам параметр функции не должен иметь имя. Я предлагаю параметры этого параметра функции в не должны быть даны имена ...

// this 
func = (f: (number => number => number)) => ... 

// not this 
func = (f: (foo: number) => (bar: number) => number)) => ... 

Почему? так как f не знает параметров функции, которую я буду предоставлять.

// for the record, i would never name parameters like this 
// but for those that like to be descriptive, there's nothing wrong with these 
const add = (addend: number) => (augend: number) => number ... 
const sub = (minuend: number) => (subtrahend: number) => number ... 
const divide = (dividend: number) => (divisor: number) => number ... 
const mult = (multiplicand: number) => (multiplier: number) => number ... 

// I could use any of these with my func 
func (add ...) 
func (sub ...) 
func (divide ...) 
func (mult ...) 

Я не мог представить имена для f «s параметров в func если бы я попробовал! Потому что кто знает, какую функцию я буду использовать? Все они уместны.

Если я пытаюсь поставить имена на них, я сукно воображения пользователя о том, что функция способна ...

// maybe the user thinks only a division function can be specified (?) 
func = (f: (dividend: number) => (divisor: number) => number) => ... 

dividend и divisor не являются хорошей подгонкой здесь, потому что любой из перечисленных функций выше будет соответствовать. В лучшем я мог бы сделать это

// provide generic name for f's parameters 
func = (f: (x: number) => (y: number) => number) => ... 

Но тогда какой смысл? Это не похоже на то, что x и y становятся связанными идентификаторами. И x и y не добавили описания - что, я полагаю, приводит меня к моим точкам: они не означают, чтобы иметь имя или описание. f имеет ноль знание того, как мы можем его использовать, но это не имеет значения; до тех пор, пока он имеет интерфейс (number => number => number), то есть все мы заботимся о. И это самая полезная информация, которую мы можем предоставить пользователю нашего func относительно параметра f.

"Было бы довольно запутанным для функции, как:

foo(cb: (number, number) => (number, string) => boolean) 

Что это делает" - unional

Одинаковые точные рассуждения применяются здесь. Помимо того факта, что (cb: (number, number) => (number, string) => boolean)) является плохо спроектированной функцией (сколько полезных функций четвертого типа (4-арности) смешанного типа вы можете назвать?), это не пункт. f не может притворяться, что знает дескрипторы о бесчисленных функциях, которые я мог придумать, используя такую ​​подпись.

Так что мой вопрос, почему, черт возьми, я должен указать открыто бессмысленных имена для параметра функции параметров?


Упражнение

Вы можете заменить _ со значимыми именами?

const apply2 = (f: (_: number) => (_: number) => number) => (x: number) => (y: number): number => { 
    return f (x) (y) 
}; 

const sqrt = (x: number): number => Math.sqrt(x); 
const sq = (x: number): number => x * x; 
const add = (addend: number) => (augend: number): number => addend + augend; 
const pythag = (side1: number) => (side2: number): number => sqrt(add(sq(side1)) (sq(side2))); 

console.log(apply2 (add) (3) (4)); // 7 
console.log(apply2 (pythag) (3) (4)); // => 5

Если нет, то вы можете сделать убедительные аргументы, почему такие имена должны присутствовать в вашей машинопись подписи?

+1

Это уже 2 значительные проблемы для одной общей функции JavaScript - если для этого нет разумного средства, я честно не вижу, как TypeScript полезен для людей. – naomik

+0

не было проблемы для объявления функции сортировки для массива в TS когда-либо: 'sort (значения: T [], compare: (one: T, another: T) => number): T []' –

+0

Мы определяем «проблему» иначе - 'one' и' another' являются неиспользуемыми (* бесполезными *) идентификаторами. Они недоступны для использования функцией и служат только для того, чтобы сделать сигнатуру функции дольше и сложнее, чем она должна быть. – naomik

ответ

1

Трудно писать определения каррирования, по крайней мере, таким образом, чтобы читать.
Я хотел бы сделать это, чтобы извлечь подписи за пределами декларации функции, насколько это возможно, что-то вроде этого:

type Ord = string | number; 
type ThirdFunction = (objs: Ord[]) => boolean; 
type SecondFunction = (objs: Ord[]) => ThirdFunction; 
type FirstFunction = (fn: (o: Ord) => (o: Ord) => boolean) => SecondFunction; 

const arrayCompare: FirstFunction = f => ([x,...xs]) => ([y,...ys]) => { 
    ... 
} 

(code in playground)

Я также удалил declare вы имели перед псевдонимом Ord типа , в этом нет необходимости. И вы можете найти лучшие имена для типов.
Другое дело в том, что вам не нужно указывать boolean здесь:

const eq = (x: Ord) => (y: Ord) : boolean => x === y; 

Может быть:

const eq = (x: Ord) => (y: Ord) => x === y; 

Или вы могли бы выразить функцию с помощью одного type декларации. Читаемость довольно приличная, все рассмотрено.

type Ord = number | string; 

type arrayCompareFunc = (f: (x: Ord) => (y: Ord) => boolean) 
         => (xs: Ord[]) 
         => (ys: Ord[]) 
         => boolean; 

const arrayCompare: arrayCompareFunc = f => ([x,...xs) => ([y,...ys) => { 
    ... 
}; 
+0

Я полагаю, что 'boolean' может подразумеваться из-за' return _ === _'? – naomik

+0

Да, компилятор сам это сообщает. –

+1

Вместо публикации отдельного ответа я сделал редактирование, которое демонстрирует консолидация трех типов данных функций в одну. Надеюсь, вы согласитесь, что она поддерживает оригинальный дух вашего ответа. Спасибо за вашу помощь. Я думаю, что это будет так близко, как мы собираемся. – naomik

-1
  1. это очень непрактично способ использования функций в JS
  2. TS сосет дженериков на ссылки функции, так много интуиции от Haskell просто не работает здесь

обратно на ваш вопрос, вы должны указать имена, потому что синтаксис TS таков, что требует от вас этого, рациональная часть состоит в том, что имя параметра передает дополнительное значение, если только этот тип не делает этого

+0

* «Это очень непрактичный способ использования функций в JS» *. Возможно, система типов TypeScript негибкая или невыразительная, но это не влияет на практичность или полезность функций общего или высшего порядка. * Именование * параметров, где их не нужно называть, совершенно глупо. В моем примере здесь вы даже не можете использовать идентификаторы, которые вы должны предоставить в области «arrayCompare» - единственный контекст, в котором этот идентификатор имеет значение, находится в самой функции более высокого порядка; в этом случае 'eq'. – naomik

+0

Я не имел в виду более высокие функции, когда я сказал непрактично, что я имел в виду, это currying, у которого есть поддержка первого класса в haskell, но слишком громоздкая и дорогая в JS (заметьте, я пока не сказал ни слова о TS) –

+0

Currying не громоздкий в JavaScript tho ... :( – naomik

0

Когда вы укажете (f: (Ord) => (Ord) => boolean), все TypeScript видит, что вы указываете функцию с одним аргументом с именем Ord. Вы не указали тип.

EDIT: Я вижу, что это ограничение в текущем TypeScript. Запрос подан здесь: https://github.com/Microsoft/TypeScript/issues/14173

Для поддержки этого синтаксиса компилятору (и языковой службе) необходимо будет ввести сами имена.

Рассмотрим, когда код используется: image

Это обеспечивает тот же синтаксис, как функция должна быть определена в машинопись. т.е. (name: Type) => .... Если имя не введено, это будет очень запутанным для пользователя.

С другой стороны, если аргументы несут какое-либо конкретное значение, стоит ИМО указать имя аргумента, чтобы пользователь знал, что делать.

Было бы довольно запутанным для функции, как:

foo(cb: (number, number) => (number, string) => boolean)

Что это делает?

+1

Мой вопрос адресован именно этому ... – naomik

+0

Да, но это синтаксис TypeScript. Это неинтрузивно. hout типы, TypeScript - это просто JavaScript. Имя переменной в JavaScript не используется и должно использоваться для указания типа. :) – unional

+0

Я понимаю, что вы пытаетесь помочь, но это не отвечает на мой вопрос.Рассказывая мне * «TypeScript - это просто Javascript» *, это то, что я уже знаю :( – naomik

-2

Так что мой вопрос, почему, черт возьми, я должен указать открыто бессмысленные имена параметров параметров функции?

Разве они не имеют смысла? Есть ли даже открыто? Я могу думать о по крайней мере три веские причины, почему называя параметры имеет смысл:

Консистенция

Это, как вы определяете типы недвижимости в машинописном:

class Person { 
    public firstName: string; 
    public lastName: string; 
    public age: number; 
} 

Это, как вы указать переменные типы:

let person: Person; 

Типы параметров:

function meet(who: Person) { 
} 

функции и возврат метод типа:

function isUnderage(person: Person): boolean { 
    return person.age < 18; 
} 

Это как вы хотите определить параметры типа функции:

let myFunc: (string, string, number) => boolean; 

... или ...

function myFuncWithCallback(callback: (string, string, number) => boolean): void {} 

... или ...

type CallbackType = (string, string, number) => boolean; 
let myFunc: CallbackType; 
function myFuncWithCallback(callback: CallbackType): void {} 

Какой из них здесь не подходит?

Всюду по TypeScript, когда вы используете типы, чтобы обозначить статическую типизацию цели, вы идете с target: type. Это легко запомнить.Если вы начнете создавать такие правила, как: Используйте синтаксис target: type во всех случаях , за исключением при определении параметров типов функций, это делает ваш язык менее последовательным и, следовательно, более сложным для изучения и использования. Это может быть технически не нужно, но согласованность является самоцелью. JavaScript полна причуд, и TypeScript наследует многие из них. Лучше не вводите никаких дополнительных несоответствий. Так что это не необычный образец, и это по уважительным причинам.

this параметры

без указания имен параметров в функциональных типов, указав this parameters in callback types бы стать еще более грязным и непоследовательными. Рассмотрим пример из связанного страницы:

interface UIElement { 
    addClickListener(onclick: (this: void, e: Event) => void): void; 
} 

Вы хотели бы это так:

interface UIElement { 
    addClickListener(onclick: (this: void, Event) => void): void; 
} 

Итак, то правило будет «использовать синтаксис target: type везде, кроме типов функций, где это это только тип в параметрах, , если не указан параметр this, то на самом деле это тот синтаксис, но только в параметре this. " Я не обвиняю дизайнеров TypeScript в том, чтобы лучше пойти с правилом «Использовать синтаксис target: type везде».

средства развития

Это то, что я получаю, когда я наведите курсор мыши на функцию в машинописном:

JQuery.each

Как бы вы хотели, чтобы прочитать func: (number, Element) => any вместо описательных имен параметров его есть? Информация о типе совершенно бесполезна сама по себе. И любой код, автоматически генерируемый из этого определения, должен был бы дать параметры (явно бессмысленные) имена, такие как param1 и param2. Вы не будете делать много друзей в сообществе разработчиков, как это.

машинопись не единственный язык, называя paramters типов функций:

C# делегатов определяются следующим образом:

delegate void EventHandler(object sender, EventArgs e); 

Delphi функциональные переменные:

type TFunc = function(x: Integer, y: Integer): Integer; 
+0

Вы не правильно читаете мой вопрос. Весь ваш ответ - аргумент счетчика за то, что я никогда не говорил. Нормальные функциональные параметры нуждаются в именах; это те идентификаторы, которые привязываются при применении функции. Я специально говорю о параметрах параметра функции *, требующих, чтобы имя было бессмысленным. Прочтите ** упражнение **, опубликованное в нижней части моего вопроса, - я имею в виду только параметры '_'. – naomik

+0

Я не вижу, как я имею в виду что-то другое, чем вы. Это превращается в пустую трату моего времени. – Sefe

+0

Я не говорю, что свойства объекта не должны иметь имен. Я не говорю, что параметры функции не должны иметь имен. Я говорю, что параметр функции * * параметров не должен иметь имен. Глядя на упражнение, вы видите «f», «x» и «y»? Ваше сообщение читает, поскольку я говорю, что параметры * no * должны иметь имена, но это определенно * не * то, что я говорю. Это обычные функциональные параметры, и им абсолютно нужно имя. Я говорю конкретно о '' '' '' '' '' '' '' '' '' '' '' '' '' '' 'имена * * * они не имеют никакого смысла. – naomik