Scala

Если вас интересует функциональное программирование, в этой статье вы найдете несколько важных аспектов этого направления и особенностей его работы в Scala.

Классические программы порождают концептуальные ограничения на использование модульной организации. Функциональные языки минимизируют эти ограничения. —Джон Хьюз, «Почему функциональное программирование значимо»

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

Что еще более важно, эта парадигма является естественной для параллельных вычислений. Уже в течение нескольких лет прирост скорости процессоров остается небольшим. Тем не менее, системы работают быстрее за счет роста числа ядер, работающих параллельно.

Почему Scala?

Scala – это язык с полной поддержкой функционального программирования, а также объектно-ориентированной парадигмы. Это один из наиболее широко используемых языков в функциональном сообществе, на высоком уровне наряду с таким языками как F# и Haskell, Clojure и других. Scala исполняется на JVM и отлично сочетается с Java, Groovy, Clojure и т.д.

Чистые функции

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

def sin(value: Double): Double

Нечистые функции

Нечистые функции позволяют влиять на внешний и/или внутренний контекст.

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

def writeToFile(fileName: String, text: String): Unit
def insertToDB(user: User): User

Функции высшего порядка

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

  • принять функцию, вернуть значение
  • принять значение, вернуть функцию
  • принять функцию, вернуть функцию
class List[T] { 
    // This Function takes anoter Function called "fn" as a parameter
    // that it's applied to each of the elements within the List
    // and filters them if fn returns false for that element.
    def filter[T](fn: T => Boolean): List[T]
}

val list: List[Int] = // ... initialized with [1, 2, 3, 4, 5, 6, 7, 8, 9]
val odds: List[Int] = list.filter(v => v % 2 != 0)
// returns: [1, 3, 5, 7, 9]

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

def isOdd(valor: Int): Boolean = {
    valor % 2 != 0
}

// let's define how we're going to filter
// this list using isOdd Function
val odds: List[Int] = list.filter(v => isOdd(v))
// returns: [1, 3, 5, 7, 9]

Неизменяемость

Неизменяемость относится к данным внутри объекта, который не может быть изменен после его создания.

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

Эти примеры сравнивают преимущества, предлагаемые Scala в отличие от объектно-ориентированного языка, подобного Java,

// Java
// ... imports 
public class Car {

    private final String _color;
    private final String _model;
    private final String _brand;
    private final Date _year;

    public Car(String color, String model, String brand, Date year) {
        _color = color;
        _model = model;
        _brand = brand; 
        _year = year;
    }

    public String getColor() {
        return _color;
    }

    public String getModel() {
        return _model;
    }

    public String getBrand() {
        return _brand;
    }

    public Date getYear() {
        return _year;
    }
}
// Scala

case class Car(color: String  model: String  brand: String  year: Date)

Монады в Scala

Монада — это интерфейс, который просто определяет единый формат для составления данных.

Монады должны подчиняться определенным правилам. Scala не столь строга с этими законами, а ее ориентированность более практична. С точки зрения языка единственными чертами, которые должна иметь Монада, являются функции высокого порядка flatMap и map. Они предоставляют инструмент для составления данных, которые в них содержатся, с помощью применения функции.

Вероятно, проще объяснить это на примере,

class Monad[A](value: A) {
    // receives a Function 'f' that transforms any 'A' value into 'B' value  
    def map[B](f: A => B): Monad[B] = new Monad(f(value))
    
    // receives a Function 'f' that transforms any 'A' value into another 'Monad[B]' 
    def flatMap[B](f: A => Monad[B]): Monad[B] = f(value)
}

val firstMonad  = new Monad(4)
val secondMonad = new Monad(6)

val resultingMonad = firstMonad.flatMap { x => 
    secondMonad.map { y => 
        y + x 
    }
} 

// returns: Monad[Int] = 10

Представьте себе на мгновение, что все известные нам коллекции является Монадой (в терминах Scala). Тогда мы можем составлять внутренние данные в этих коллекциях просто с помощью функции map или flatMap.

def plus2(value: Int): Int = {
    value * 2
}

val lista   = Seq( 1, 2, 4, 5, 6, 7, 8, 9, /*...*/ 100, 101 )
val listaX2 = lista.map { v => plus2(v) }
// results: Seq(2, 4, 6, 10, 12, 14, 16, 18, /*...*/ 200, 202)

Резюме

В этом сообщении просто описывается некоторые из наиболее важных аспектов функционального программирования на Scala. Существует множество тем, требующих изучения. Например, специфические для языка Монады (Option, Future & Try) или «for-генераторы»(«for-comprehension») и «применить» («apply») методы для упрощения программ.

Кроме того, важно узнать больше о некоторых из наиболее широко используемых фреймворках, таких как Play!, Finagle и Akka.

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

Перевод статьи Daniel Valera: Functional Programming with Scala: an Intro