Как продвигать ФП

Что имею?

Frontend

Backend

  • Scala
  • Java 8
  • Python
  • Ruby
  • PHP
  • Erlang

JavaScript

Лексический разбор на Scala

abstract class Matcher {
  def apply(code: String, position: Int): Option[Int]
}


// Терминальный символ
final case class StringMatcher(pattern: String)
  extends Matcher {

  def apply(code: String, position: Int) =
    Some(position + pattern.length)
      .filter(next => next <= code.length
        && code.substring(position, next) == pattern)
}

// Оператор | в PEG
final case class ChoiceMatcher(first: Matcher,
                               second: Matcher) extends Matcher {
  def apply(code: String, position: Int) =
    first(code, position).orElse(second(code, position))
}

// Пример использования ("hello"* & "world") | "foobar"

choice(sequence(zeroOrMore(string("hello")), string("world")), string("foobar"))

Лексический разбор на Scala

  def tokenize(input: String) = {
    val rules = this.rules.toIterable

    var output = List.empty[Token]
    var position = 0
    var begin = true
    while (position < input.length) {
      val start = position

      var application = Option.empty[(String, Int)] // Опция

      while (position < input.length && application.isEmpty) {
        application = rules // ленивые коллекции
          .map(rule => (rule._1, rule._2(input, position)))
          .find(candidate => candidate._2.isDefined) // компактные лямбды
          .map(result => result.copy(_2 = result._2.getOrElse(position + 1)))

        if (application.isEmpty) position += 1
      }

      if (start < position)
        output ::= Token.unknown(input.substring(start, position))

      for (successful <- application) { // избегаем NPE
        output ::= new Token(
          kind = successful._1,
          value = input.substring(position, successful._2)
        )

        position = successful._2
      }
    }

    output.reverse
  }

Что хочу?

  • Коллекции данных
  • Ленивые вычисления
  • Опции
  • Модульность
  • Параллельные вычисления
  • Удобный синтаксис для лямбд

Java Script "из коробки"

Функции:

  • function() {...} - лямбда
  • bind - каррирование

 

Массивы:

  • map, filter - list comprehension
  • reduce, foldl/foldr - катаморфизмы
  • every, find - предикаты

 

Расширяемый синтаксис:

Array.prototype.foo = function() {...}

[1, 2, 3].foo

Коллекции данных в JS

Underscore.js

  • zip
  • range
  • objecy <-> array

Ленивые вычисления в JS

wu.js

function* fibs() {
  let a = 0;
  let b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

wu(fibs())
  .filter(isEven)
  .takeWhile(lessThanTen)
  .forEach(console.log.bind(console));

wu.count(5) // (5, 6, 7, 8, 9, 10, ...)

Maybe в JS

[] - Nothing
[x] - Some

wu([])
  .map(function(x) { return x + 1; })
  .filter(function(x) { return x > 0; })
  .forEach(function() {
    // case Some
  });

Модульность

Один файл - изолированный модуль

// файл API.js

module.exports = {
    inc: functino(x) { // публичная функция
        return x + 1;
    };
}

function internalFunc() { // невидна извне
    // ...
};

С помощью browserify:

// файл main.js

var API = require("API.js")

console.log(API.inc(100)); // 101

Параллелизм в JS

Q.js:

Q
  .all([
    fooFunction,
    booFunction
  ])
  .then(function(result) {
    var
      resultOfFoo = result[0],
      resultOfBoo = result[1];
    
    return result[0] + result[1]
  })
  .then(function(result) {
    console.log(result);
  });

async.js:

async
  .parallel(
    [
      fooFunction,
      booFunction
    ],
    function(result, callback) {
      var
        resultOfFoo = result[0],
        resultOfBoo = result[1];

      setTimeout(function() {
        callback(resultOfFoo + resultOfBoo)
      }, 0)
    }
  );

Удобные лямбды

JavaScript

application = rules
  .map(function(rule) {
    return ...
  })
  .find(function(candidate) {
    return ...
  });

CoffeeScript

application = rules
  .map rule -> ...
  .find candidate -> ...

How to promote FP [in Russian]

By Ilya Lakhin

How to promote FP [in Russian]

  • 2,868