Функтор ассоциативного закона путаницы

1

Меня смутило ассоциативное право функтора (Endofunctor) в течение нескольких недель.

Я знаю, что каждый эндофуктор формирует композицию/ассоциативную особенность.

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

h∘(g∘f) = (h∘g)∘f = h∘g∘f

Давайте еще раз взглянем на закон композиции в JavaScript:

Учитывая функтор, F:

const F = [1, 2, 3];

Следующие эквиваленты:

F.map(x => f(g(x)));
// is equivalent to...
F.map(g).map(f);

Однако, как показано ниже, код, особенно поздняя часть:

console.log("===================");
const take1 = a => a //any
  .map(g)
  .map(f)
  .map(trace);

это приведет к ошибке типа:

//  const r3 = v.map(take1); //TypeError: a.map is not a function

и, безусловно:

const take2 = a => Identity(a) //Identity(any)
  .map(trace)
  .map(g)
  .map(trace)
  .map(f)
  .map(trace);
const r4 = v.map(take2);

работает.

Я ошибаюсь в функции take2, необходимо преобразовать тип аргумента: a to Identity(a) самом деле не Identity(a) функции.

Я также понимаю, что Монады должны избегать этой проблемы с композицией, и мне интересно, просто ли это из-за отсутствия "закона права Лоди-права Монадов", а законный ассоциативный закон все еще выполняется или могут быть разные слои ассоциативного законов и функтора, очевидно, выполняется некоторый слой ассоциативного закона, но другой слой ассоциативного закона нарушается, как видно из вышеприведенного примера.

Вы можете уточнить?

Образец кода, конечно, есть в JavaScript, но я по-прежнему отмечаю Haskell, так как сообщество сильное в этой теме, поэтому, пожалуйста, извините меня.

Спасибо.

const trace = x => {
  console.log(x);
  return x;
};
const Identity = value => ({
  map: fn => Identity(fn(value)),
  valueOf: () => value,
});
const u = Identity(2);
const f = n => n + 1;
const g = n => n * 2;
// Composition law
const r1 = u
  .map(x => f(g(x)));
const r2 = u
  .map(g)
  .map(f);
r1.map(trace); // 5
r2.map(trace); // 5
console.log("===================");
const take1 = a => a //any
  .map(g)
  .map(f)
  .map(trace);
const v = Identity(100);
//  const r3 = v.map(take1); //TypeError: a.map is not a function
const take2 = a => Identity(a) //Identity(any)
  .map(trace)
  .map(g)
  .map(trace)
  .map(f)
  .map(trace);
const r4 = v.map(take2);

PS/EDIT:

Еще одна причина спросить об этом, если мы просто рассмотрим последовательность функций f/g/h в качестве последовательности данных и вырезаем и вставляем как строки, структура становится:

h∘(g∘f) != (h∘g)∘f != h∘g∘f

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

  • 1
    Проблема, о которой вы спрашиваете, на самом деле не имеет ничего общего с законом ассоциативности.
  • 0
    Также предложение: чтобы полностью погрузиться в функциональное мышление, лучше не использовать trace а только отображать конечные значения. (Не то, что Haskellers не используют trace для отладки или, скорее, traceShowId , но только как хак, чтобы узнать о некоторых деталях расхождения значений. Сначала всегда идет этап проверки типов.)
Теги:
haskell
monads
associative
functor

2 ответа

3

Попробуем разобраться в терминологии.

В теории категорий категория C состоит из объектов и морфизмов (также называемых стрелками). Функтор F между двумя категориями C и D, записанный F: C → D, отображает объекты C в объекты D и морфизмы C в морфизмы D.

Вы можете составлять функторы (очевидным образом).

Композиция функторов ассоциативна: если F: C → D, G: D → E и H: E → F, их состав (который является функтором от C до F) не нуждается в скобках.

Вы также можете составить морфизмы (внутри категории). Состав морфизмов также ассоциативен.

Более того, функтор должен учитывать композицию морфизмов (т.е. F (g∘f) = F (g) ∘F (f)). Это полностью отличается от ассоциативности.

Энтофуктор является функтором из некоторой категории в ту же категорию, F: C → C.

В Javascript нет типов, поэтому, чтобы превратить их в категорию, представьте один объект (какой-то универсальный тип) как единственный объект. Морфизмы - это функции с одним аргументом.

Теперь это

const F = [1, 2, 3];

не является функтором: вы не сказали, как объекты отображаются (хотя выбора не так много), вы не сказали, как отображаются морфизмы (функции Javascript).

Тем не менее, вы можете определить функтор Array для Javascript следующим образом:

а) Наш универсальный тип привязывается к самому себе. (Если Javascript имел типы, мы сопоставляли бы тип t с типом "массивы t").

b) Функция f отображается в функцию из массива в массив, применяя ее "поточечно". Это карта на массивах в Javascript: Array (f) = (x => x.map(f)) (используя => для функций).

Обратите внимание, что массив не является чем-либо, что вы можете полностью записать в Javascript.

Теперь этот функтор учитывает композицию морфизмов (функции Javascript) и т.д. И т.д., Так как легко получается. И один промежуточный шаг в этом случае будет состоять в том, что действительно x.map(f).map(g) = x.map(y => g (f (y)) (неформально, не имеет значения, если вы сначала примените f к все элементы массива x, а затем примените g ко всем из них или если вы сразу примените g после f ко всем элементам массива).

Это также было бы ассоциативно, если мы скомпоновали бы его с другими функторами (хотя у нас еще нет примеров для других функторов в Javascript).

На данный момент оставь монады из картины.

Это помогает?

  • 0
    «В Javascript значения образуют категорию». Это не имеет смысла, не так ли? Какие значения должны быть в категории? Они, конечно, не являются объектами этой категории.
  • 0
    @ leftaroundabout: Вы правы, я запутался, потому что нет типов. Ред.
2

С const r3 = v.map(take1) вы уже const r3 = v.map(take1) в один слой map-yness, вы входите в функтор, если хотите. Внутри функтора у вас есть чистые, развернутые значения. Но take1 сам пытается снова использовать map на этих значениях! Это может работать, но только если значения сами представляют собой значения функтора - например, вложенный Identity.

Чтобы использовать функцию, которая уже использует map внутри, просто примените ее к значению functor:

const f = n => n + 1;
const g = n => n * 2;

const Identity = value => ({
  map: fn => Identity(fn(value)),
  valueOf: () => value,
});

const take1 = a => a //any
  .map(g)
  .map(f);

const v = Identity(100);
const r3 = take1(v);

console.log(r3.valueOf());
  • 0
    Спасибо, но это то, что я знаю, и я подумал, что разъяснил в вопросе. Пока, к сожалению, вы ничего не отвечаете по этой теме.
  • 3
    Ну, это не совсем ясно , что ваш вопрос.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню