LINUX.ORG.RU

Вопрос про монаду Identity

 , ,


0

3

Есть вот такая вот реализация этой монады

Identity = function(value) {
   this.value = value
}
 
Identity.prototype.bind = function(transform){
   return transform(this.value)
}

new Identity(5).bind(function(value){
   return new Identity(6).bind(function(value2){
       return new Identity(value + value2)
   })
})

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


IIdentity = function(value){this.value = value} // improved version
IIdentity.prototype.transform = function(transformer){return new IIdentity(transformer(this.value))}

new IIdentity(5).transform(function(x){return x + 6})

?



Последнее исправление: newquestion (всего исправлений: 2)

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

нет

тебе же вот пишут:

Computationally, there is no reason to use the Identity monad instead of the much simpler act of simply applying functions to their arguments

jtootf ★★★★★
()

Identity нужна в качестве базовой монады в трансформерах. Типа такого.

type Reader a = ReaderT Identity a

И всё.

hateyoufeel ★★★★★
()
Ответ на: комментарий от jtootf

Computationally

А что это значит. То есть, я я понял, что калька «вычислительно», но какой семантический аналог в русском?

newquestion
() автор топика
Ответ на: комментарий от jtootf

there is no reason to use the Identity monad instead of the much simpler act of simply applying functions to their arguments

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

newquestion
() автор топика
Ответ на: комментарий от newquestion

The purpose of the Identity monad is its fundamental role in the theory of monad transformers. Any monad transformer applied to the Identity monad yields a non-transformer version of that monad.

jtootf ★★★★★
()
Ответ на: комментарий от jtootf

И что с того? Обе версии делают то же самое — елдят нетрансформированную версию этой монады, иными словами, тупо возвращают новый объект, и сеттят ему значение, переданное в качестве аргумента. Зачем лапша из замыканий?

newquestion
() автор топика
Ответ на: комментарий от jtootf

Собственно, там сама монада (конструктор) нужна только для того, чтобы в вытащить из нее значение в замыкание. Что это за цирк ваще?:)

newquestion
() автор топика

А вот написал бы на языке с типами, сразу понял бы, что второй вариант не эквивалентен первому.

anonymous
()
Ответ на: комментарий от newquestion

Зачем лапша из замыканий?

Ты в комментах к собственной лапше спрашиваешь зачем ты её написал? Шизофреник что-ли?

hateyoufeel ★★★★★
()

Начнём с того, что твои примеры делают разные вещи. В исходном варианте имеются new Identity(5) и new Identity(6), а из них уже делают Identity(11). В твоём варианте есть new Identity(5) и число 6.

Miguel ★★★★★
()
Ответ на: комментарий от newquestion

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

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от Miguel

в исходном варианте new Identity(6) нужен только лишь для того, чтобы забрать оттуда 6 и загнать его в замыкание. Это масло масляное.

newquestion
() автор топика
Ответ на: комментарий от Miguel

имеется в виду функция, которая подается в качестве аргумента методу transform, по аналогии с bind, в первом варианте. Я просто подумал что название object.transform(transformer) выглядит логичней.

newquestion
() автор топика
Ответ на: комментарий от newquestion

Предусматривается получение Identity(11) из Identity(5) и Identity(6), с использованием только new и bind (а также функций, работающих не с Identity).

Miguel ★★★★★
()
Ответ на: комментарий от newquestion

BTW, то, что ты обозвал transform — это называется map. map можно выразить через bind, а вот bind через map — нет.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

Предусматривается получение Identity(11) из Identity(5) и Identity(6), с использованием только new и bind (а также функций, работающих не с Identity).

new IIdentity(5).transform(function(x){return x + new IIdentity(6).value})

можешь название метода transform заменить на bind, если так больше нравится.

newquestion
() автор топика
Ответ на: комментарий от Miguel

Опять же, смысл этого всего непонятен. Этот код похож на тавтологию. Там new Identyty в комплекте с bind используется только для того, чтобы вытащить значение в замыкание, создаваемое transform. то есть этот код де-факто делает вот это

;(function(value){return function(value2){return new Identity(value + value2)}})(5)(6)

newquestion
() автор топика
Ответ на: комментарий от newquestion

".value" — это мимо.

Идея в том, что вместо Identity можно подставить любую монаду. Вообще любую.

value есть только у Identity.

И да, в оригинале нет никакого transform.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

value есть только у Identity

никто не мешает сделать класс «Monad», от которого наследовать любую монаду или другой класс монад, и в любом экземпляре любого класса/сабкласса будет value.

в оригинале нет никакого transform.

В каком оригинале? В том что в старт-топике? Там bind в этой роли, какая разница.

newquestion
() автор топика

Объясни мне, зачем ты несколько лет настойчиво пытаешься затащить монады в динамически типизированное говно?

mix_mix ★★★★★
()
Ответ на: комментарий от mix_mix

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

newquestion
() автор топика
Ответ на: комментарий от newquestion

никто не мешает сделать класс «Monad», от которого наследовать любую монаду или другой класс монад, и в любом экземпляре любого класса/сабкласса будет value.

Массив — это монада. Назови его value.

x3al ★★★★★
()
Ответ на: комментарий от x3al

Назови его value

А причем тут название? речь была о том, что любая монада содержит в себе value, а не называется value.

newquestion
() автор топика
Ответ на: комментарий от newquestion

Эмм. Допустим, у тебя есть массив a = [0, 1, 2]; и функция square(x) { return x * 2; }. Что ты сделаешь для того, чтобы заставить square(x) работать с массивом a, не меняя функцию? И чтобы та же функция заработала с, допустим, Maybe(1)?

Суть монад в том, что ты определяешь себе bind/map/lift/что_угодно и после этого твои функции работают с чем угодно.

x3al ★★★★★
()
Последнее исправление: x3al (всего исправлений: 1)
Ответ на: комментарий от x3al

Суть монад в том, что ты определяешь себе bind/map/lift/что_угодно и после этого твои функции работают с чем угодно.

Что-то сильно смахивает на обычный полиморфизм


Monad = {
 value: null,
 create: function(value){
    var o = Object.create(this)
    o.value = value
    return o
  }
}

MyArray = Monad.create()
MyArray.bind = function(transformer){
    return this.create(this.value.map(transformer)) 
}

MayBe = Monad.create()
MayBe.bind = function(transformer){
  return this.value? this.create(transformer(this.value)) : this.create()
}

square = function(x){return x * 2}
sum = function(x){return x * x}

MyArray.create([1,2,3]).bind(square).bind(sum).print
MayBe.create(1).bind(square).bind(sum).print
MayBe.create().bind(square).bind(sum).print

//>>>> { value: [ 4, 16, 36 ] }
//>>>> { value: 4 }
//>>>> { value: undefined }

Это?

newquestion
() автор топика
Ответ на: комментарий от x3al

с sum я там лоханулся

square = function(x){return x * 2}
sum = function(x){return x + x}

MyArray.create([1,2,3]).bind(square).bind(sum).print
MayBe.create(1).bind(square).bind(sum).print
MayBe.create().bind(square).bind(sum).print


//>>>> { value: [ 4, 8, 12 ] }
//>>>> { value: 4 }
//>>>> { value: undefined }

//fixed

но не суть

newquestion
() автор топика
Ответ на: комментарий от newquestion

никто не мешает сделать класс «Monad», от которого наследовать любую монаду или другой класс монад, и в любом экземпляре любого класса/сабкласса будет value.

С чего вдруг? С того, что тебе так захотелось?

Интерфейс Monad — это bind и return. В этом JS вместо return используют new. Никакого value в интерфейсе монады нет.

Там bind в этой роли, какая разница.

Нет, не в этой.

Miguel ★★★★★
()
  • Монада — это Functor + Applicative + Monad. Точнее, можешь считать, что Monad наследуется от Applicative, а Applicative — от Functor.
  • Functor — это то, что умеет fmap (можешь обозвать его map). map берёт функцию, работающую с необёрнутыми значениями, и применяет её к тому, что внутрь него завёрнуто. incBy6 = function(x) {return x + 6} — это функция, работающая с необёрнутыми значениями. Чтобы применить её, нужен map (не bind).
    map = function(Functor, func) { return Functor.map(func) }
    map(Identity(5), incBy6); // Identity(11)
  • Applicative — это то, что умеет liftA (я хз, как это обозвать в жабоскрипте). liftA умеет взять завёрнутую функцию, которая работает с необёрнутыми значениями и map'нуть её к функтору.
  • Monad — это то, что умеет bind. bind умеет взять функцию, возвращающую обёрнутое значение, и применить её к тому, что завёрнуто в ней.
  • Для того, чтобы определить монаду, достаточно определить bind и return. return возвращает, грубо говоря, новый instance от значения, которому скормишь.

Обычно юзаешь самое минимальное из того, что тебе нужно. В монаду можно завернуть как вычисления (Maybe — самый известный пример: проверка на null внутри, но это может быть и Either), так и контейнер (List/Array/что там ещё).

Вроде как всё. Поэтому твой IIdentity.prototype.transform определяет функтор Identity вместо монады (ибо transform — это fmap).

x3al ★★★★★
()
Последнее исправление: x3al (всего исправлений: 1)
Ответ на: комментарий от x3al

Впрочем, я — говно и перепутал порядок аргументов.

Энивей, всякие map можно спокойно определять через bind.

x3al ★★★★★
()
Последнее исправление: x3al (всего исправлений: 1)

IIdentity = function(value){this.value = value} // improved version

Ну все, убираю форматирование и запиливаю импрувид!

ritsufag ★★★★★
()
Ответ на: комментарий от x3al

Identity(11)

Что это значит? Ты возвратил вызов функции? Как ты себе это представляешь? У тебя на самом деле тут должен вернуться объект.

Applicative — это то, что умеет liftA (я хз, как это обозвать в жабоскрипте). liftA умеет взять завёрнутую функцию, которая работает с необёрнутыми значениями и map'нуть её к функтору.

а зачем ее дополнительно маппить к функтору, если ты в предыдущем коде уже это сделал? map(Identity(5), incBy6) — вот это что тогда было по твоему?

Кроме того, зачем ты вот это делаешь это через левую функцию map, если это можно сделать напрямую Identity(5).map(incBy6)?

newquestion
() автор топика
Ответ на: комментарий от newquestion

Кроме того, зачем ты вот это делаешь это через левую функцию map, если это можно сделать напрямую Identity(5).map(incBy6)?

Можно, но левую функцию удобнее запихнуть в compose/pipe, хотя я ошибся с порядком аргументов. В запущенных случаях появляется dot = function(prop, obj) { return obj[prop]; } (обычно она каррируется).

а зачем ее дополнительно маппить к функтору, если ты в предыдущем коде уже это сделал? map(Identity(5), incBy6) — вот это что тогда было по твоему?

incBy6 — не завёрнутая функция. Identity(incBy6) — завёрнутая функция. Just(incBy6) — ещё одна. Applicative может работать с этой фигнёй, функтор — нет.

У тебя на самом деле тут должен вернуться объект.

Нужно же как-то обозвать монаду Identity с завёрнутым внутрь 11.

x3al ★★★★★
()
Ответ на: комментарий от x3al

И да, нафиг нужны compose/pipe: у тебя есть

MyArray.create([1,2,3]).bind(square).bind(sum).print
MayBe.create(1).bind(square).bind(sum).print
MayBe.create().bind(square).bind(sum).print
, с compose можно запихнуть bind(square).bind(sum).print в одну функцию (скажем, f) и потом делать f(MayBe(1)), f(MayBe()), f(MyArray([1, 2, 3])) (или даже f([1, 2, 3]) поскольку map есть у родного JS-массива, а значит он достаточно функтор, bind там нафиг не нужен).

x3al ★★★★★
()
Последнее исправление: x3al (всего исправлений: 2)

Ты зачем прозрачную купил? Белой не было?

Aswed ★★★★★
()
28 ноября 2015 г.
Ответ на: комментарий от newquestion

new IIdentity(5).transform(function(x){return x + new IIdentity(6).value})

И вроде всё нормально, пока у тебя Identity. А возьми какую-нибудь Maybe:

function Just(value) {
  this.value = value
}

function Nothing() {}

Just.prototype.bind = (fn) => fn(this.value)

Nothing.prototype.bind = (_) => this

// просто пример функции, возвращающей Maybe
function sqrtMaybe(x) {
  return x >= 0 ? new Just(sqrt(x)) : new Nothing()
}

sqrtMaybe(25).bind((x) =>
  sqrtMaybe(36).bind((y) =>
    new Just(x + y)))  // Just(11)

sqrtMaybe(-1).bind((x) =>
  sqrtMaybe(36).bind((y) =>
    new Just(x + y)))  // Nothing

А если ты будешь пытаться вызвать value у Nothing, не получится ничего хорошего.

P. S. А благодаря sweet.js пример выше можно было бы записать как-то так:

sqrtMaybe(25)
  >>= (x) => sqrtMaybe(36)
  >>= (y) => new Just(x + y)
Могу скинуть макросы, если интересно

KolesnichenkoDS
()
Ответ на: комментарий от Virtuos86

автора топика забанили

Не заметил, ну да ладно

В общем, если кому-нибудь интересно, то вот: http://pastebin.com/qBvzkUeg

Надо бы ещё do-нотацию туда. Чуваки из Curiosity Driven с генераторами извратились, с макросами оно выглядит вот так: http://pastebin.com/DCLGfh46. Но думаю, можно и полностью на макросах это сделать.

KolesnichenkoDS
()
Ответ на: комментарий от anonymous

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

Virtuos86 ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.