All streams
Search
Write a publication
Pull to refresh
12
0
Send message
Ну для этого как минимум операции должны быть атомарными, а скорость роста всех возможных перестановок явно отобьет охоту проверять все возможные ситуации :)
Я не говорю еще про инвалидацию кэша процессоров, оптимизации компиляторов и пр.низкоуровневые вещи, меняющие выполнение операций.

Да, жизнь боль :(
Еще красному часто противопоставляют зеленый :)
И будет логично, что переопределить метод интерфейса будет невозможно

Как раз-таки в этом и вся соль, что можно переопределить метод по умолчанию.
Я думаю это попытка воплотить typeclass из Haskell.
Самый простой пример:
interface Eq {
  bool Equal(a, b) { return !NotEqual(a, b);}
  bool NotEqual(a, b) {return !Equal(a, b);}
}

Таким образом для реализации интерфейсов у нас минимальный набор операций для реализации это или Equal или NotEqual. В нек-ых случаях может оказаться, что операцию NotEqual реализовать проще чем Equal, а оставшуюся операцию мы получаем «бесплатно».
Вы заявили то, что ООП поощряет чистоту, а не то что позволяет писать методы, к-ые не используют состояние.

Не нужно ходить далеко, проведите эксперимент. Попросите ваших знакомых написать сложение двух чисел.
Могу поделиться своим результатом. В большинстве случаев решение будет выглядеть примерно так:
class Sum {
   public int Value {get; private set;}
   public Sum(int initial) {
       Value = initial;
   }
   public void Add(int value) {
      Value += value; 
   }
}
Интересно каким образом?

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

Можно поинтересоваться почему в ФП не может быть const и let?
Const в них используются неявно, а let есть ничто иное как псевдоним, для некоторого выражения, наподобии того как в математики вводят дополнительные переменные

Да я ничего и не имею против for :)
На самом деле он вполне себе декларативен и если присмотреться то можно в нем увидеть монвду list в do нотации (особенно в for of)

Смысл map в том что производится трансформация над каждым элементом (можете считать что это групповая операция) и кол-во элементов на входе и на выходе обязано быть одинаковым

А я вот противоположной точки зрения
Глядя на композицию map, filter, reduce можно сразу выделить структуру даже не вникая в детали (банально если заканчивается reduce — значит результат одиночное значение, иначе — массив). С циклом же нужно полностью изучить весь код, чтобы выявить какую-то структуру.

Конечно понимаю. Я ведь даже писал, что нам на самом деле нет смысла использовать иммутабельные данные внутри reduce.

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

Если мы не используем всякие сторонние библиотеки для иммутабельных структур, то любой объект можно интерпретировать как мутабельный так и иммутабельный. И т.к. в случае reduce создается пустой объект — соответственно нет необходимости в преобразованиях вида
Иммутабельный->Мутабельный и Мутабельный->Иммутабельный
Да, действительно, забыл добавить {} :).
Вот так надо было
Object.assign({}, acc, {foo: 123});
Код без Ramda — опять грязный, мутируете acc. Если мутируете, зачем тогда вообще reduce?


Да нет же. Object.assign — создает копию. А вообще на самом деле, т.к. js однопоточный и этот объект генерируется только внутри reduce можно было смела заменить на мутации. Получили бы выигрышь в производительности :)
Вот как можно решить, используя стрелки Клейсли
Собственно решение занимает по факту 1 строчку:
const totalSallary = R.compose(R.sum, R.pipeK(departments, branches, sectors, sallary))

Ниже привел захардкоженные данные, чтобы можно было поиграться
/* Функции определящие стуктуру нашего институа. 
В них просто захордкожены данные */

//Institue -> [Department] 
//По институту возвращает список отделений
const departments = institue => ['D1', 'D2', 'D3'];

//Department -> [Branch]
//По отделению возвращает список отделов
const branches = department => {
  switch (department) {
    case 'D1': 
      return ['B11', 'B12', 'B13'];
    case 'D2':
      return ['B21', 'B22'];
    case 'D3':
      return ['B31']
  }
}

//Branch -> [Sector]
//По отделу возвращает список секторов
const sectors = branch => {
  switch(branch) {
    case 'B11':
      return ['S111', 'S112'];
    case 'B12':
      return ['S121']
    case 'B13':
      return ['S131', 'S132']
    case 'B21':
      return ['S211']
    case 'B22':
      return ['S221', 'S222']
    case 'B31':
      return ['S311']
   }
}
//Sector -> [Sallary]
//Возвращает зарплату для сектора
const sallary = sector => [+sector.slice(1, 4)];

//Institue -> Int
//Возвращает суммарную ЗП по институту
const totalSallary = R.compose(R.sum, R.pipeK(departments, branches, sectors, sallary))

totalSallary('Some institue')


Покажите теперь пожалуйста, как вы реализовываете это на ООП.
гораздо лучше — plus(objA, objB).


Это чем лучше? Естественно писать a + b, а значит a.plus(b) — более естественный способ записи. А учитывая перегрузку операторов — он вообще сводится к a + b.


Раз Вы сами заговорили про перегрузку операторов, каким образом она реализуется?
...
public static ComplexNumber operator+(ComplexNumber a, ComplexNumber b)
    {
        return new ComplexNumber(a.real + b.real, a.imaginary + b.imaginary);
    }
...


Ничего не напоминает статический метод? Да ведь это обычная функция :) Просто ее нужно ведь куда-то поместить… Статический метод, кстати это маркер того, что на самом деле нам нужна функция, а не метод.
На самом деле все просто — если мы пишем a.plus(b) — мы делегируем ответственность за операцию классу a. А чем он лучше чем класс b? (Конечно имеется ввиду что а и b разного типа).

В случае plus(a, b) типы a,b равноценны.
Используя Ramda
let group = R.pipe(R.filter(isValid),                   
                   R.groupBy(d => d.category),
                   R.mapObjIndexed(R.map(d => d.id)))

//или
R.pipe(	R.filter(isValid),
  	R.reduceBy((category, d) => category.concat(d.id), 
       	[],
       	d => d.category))
//Используем так:
group(array)


Если хотите на родных map, filter, reduce, можно что-то вроде
function group(by, selector) {  
  return function(acc, next) {
    let key = by(next),
        newItem = selector(next),
        category = acc[key]? acc[key]: [];        
    return Object.assign(acc, {[key]: category.concat(newItem)});
  }
}

array.filter(isValid)
     .reduce(group(d => d.category, d => d.id), {})


Функция group пишется один раз, и может быть использована в других местах.
ap(someOtherMonad) {//используется для работы с несколькими монадами
return someOtherMonad.map(this.__value);

Но ведь
map(f) {//Применяет функцию, но возвращает другую монаду!
return Monad.of(f(this.__value));
};

В случае ap в контейнере находится не значение, а функция. Т.е. this.__value — это функция поднятая на уровень контейнера.

P.S.
Как-то не верится что эта довольно разжёванная статья как-то подвинет в сторону понимания ФП.

Согласен с Вами. Как уже упоминал выше, если есть желание действительно разобраться почитайте это.
Если кто-то действительно хочет разобраться в ФП на js то советую почитать это. И на русском языке (но не все переведено)
Состояние объекта вы так же легко можете десериализовать

А состояние всей системы? :)

Information

Rating
Does not participate
Registered
Activity