Что такое хорошая витрина для Clojure? - PullRequest
7 голосов
/ 18 марта 2011

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

Ответы [ 3 ]

12 голосов
/ 18 марта 2011

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

Я перечислю некоторые проблемы, с которыми я вынужден иметь дело неделю или неделю с Java, и то, как я их решу или буду решать в Clojure.

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

В Java добиться неизменности очень и очень сложно. Помимо строгой практики кодирования, вам придется очень тщательно выбирать свои фреймворки и библиотеки. Также в качестве побочного эффекта вы либо напишите много кода для создания чистого и удобного API, либо просто заставите клиента справиться с ним.

final Person person = personDao.getById(id);
// I would like to "change" the person's email, but no setters... :(

В Clojure вы моделируете свои данные на основе неизменяемых структур данных, поэтому все ваши объекты являются неизменяемыми по умолчанию, и благодаря этому Clojure предлагает мощные функции, которые работают с этими структурами.

(let [person            (get-by-id person-dao id)
      person-with-email (assoc person :email email)]
  ; Use person-with-email...

Конверсия

В Java у вас есть класс домена Person с полями id, name, email, socialSecurityNumber и другие. Вы создаете веб-сервис для получения имен и адресов электронной почты всех людей в вашей базе данных. Вы не хотите выставлять свой домен, поэтому вы создаете класс PersonDto, содержащий name и email. Это было легко, поэтому теперь вам нужна функция для отображения данных с Person на PersonDto. Вероятно, что-то вроде этого:

public class PersonPopulator {
    public PersonDto toPersonDto(Person person) {
        return new PersonDto(person.getName(), person.getEmail());
    }

    public List<PersonDto> toPersonDtos(List<Person> persons) {
        List<PersonDto> personDtos = new ArrayList<PersonDto>();
        for (Person person : persons) {
            personDtos.add(toPersonDto(person));
        }
        return personDtos;
    }
}

Хорошо, это было не так уж плохо, но что, если вы хотите поместить больше данных в DTO? Ну, код конструктора в toPersonDto немного вырастет, не беспокойтесь. Что если есть два разных варианта использования, один, как указано выше, а другой, где мы хотим отправлять только электронные письма? Ну, мы могли бы оставить значение name нулевым (плохая идея) или создать новый DTO, возможно PersonWithEmailDto. Итак, мы создали бы новый класс, несколько новых методов для заполнения данных ... вы, наверное, видите, куда это идет?

Clojure, динамически типизированный язык с неизменяемыми структурами данных, позволяет мне делать это:

(defn person-with-fields [person & fields]
  (reduce #(assoc %1 %2 (get person %2)) {} fields))

(person-with-fields {:id 1 
                     :name "John Doe"
                     :email "john.doe@gmail.com"
                     :ssn "1234567890"} :name :email)
; -> {:email "john.doe@gmail.com", :name "John Doe"}

И для манипулирования списком лиц:

(map #(person-with-fields % :name :email) persons)

Также было бы легко добавить специальные данные человеку:

(assoc person :tweets tweets)

И это ничего не сломало бы. В Java, если ваши объекты являются неизменяемыми, у них, вероятно, нет установщиков, поэтому вам придется написать много шаблонов, просто чтобы изменить одно поле (new Person(oldPerson.getName(), oldPerson.getEmail(), tweets)) или создать совершенно новый класс. Изменяемые объекты предлагают хороший API (oldPerson.setTweets(tweets)), но его сложно протестировать и понять.

Тестирование

Большая часть кода Java основана на каком-то состоянии, даже когда в этом нет необходимости. Это означает, что вы можете смоделировать это состояние, что обычно означает дополнительный шаблон и усложняется, если вы не создали свой код с учетом тестируемости. С другой стороны, тесты без насмешек, как правило, медленны и зависят от доступа к базе данных или времени, или чего-то еще, что время от времени может вас не устраивать.

При кодировании Clojure я заметил, что на самом деле мне не нужно так много состояний. Почти единственные ситуации, когда я получаю что-то извне, будь то база данных или какой-то веб-сервис.

Резюме

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

5 голосов
/ 18 марта 2011

Классическая проблема параллелизма "Спящий парикмахер" может быть хорошей.

Вот пара примеров

http://www.bestinclass.dk/index.clj/2009/09/scala-vs-clojure-round-2-concurrency.html

https://github.com/bitsai/clojure-actors/blob/master/sleeping_barber.clj

1 голос
/ 19 марта 2011

Пару месяцев назад я столкнулся с той же проблемой, и мы решили решить проблему «Моны Лизы» в Clojure.Результат был эта презентация .По сути, мы показали, что Clojure чрезвычайно полезен для решения задач, для которых «код как данные» дает элегантное решение.Например, в генетических алгоритмах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...