В предыдущей статье раскрывались некоторые базовые понятия теории типов. В этот раз мы рассмотрим обобщённые типы (generics) – необходимость появления такой абстракции, ключевые особенности и различные сценарии использования в программировании.
Scala *
Мультипарадигмальный язык программирования
Обобщённые типы. Часть 2/3. Классы типов и контейнеры
Это вторая часть обзора обобщённых типов, в которой мы расскажем о классах типов и типах-контейнерах.
Эффект Монреаля: почему языкам программирования нужен Царь стилей
Давайте представим нереалистичный сценарий, где вы выбираете язык программирования для проекта, который в перспективе станет очень большим. Допустим, это будет набор сервисов в монорепозитории, над которыми работает более 100 человек. Чтобы сделать этот сценарий ещё менее реалистичным, предположим, что вы игнорируете типичные ограничения, например, не учитываете, сможете ли использовать сборщик мусора, и впишется ли поставленная задача в конкретный стек технологий.
Пусть это будет мысленный эксперимент. Подыграйте мне. Если вы читали мою прошлую статью (англ.), то должны правильно предположить, что я бы предпочёл экспрессивный язык, ориентированный на профессионалов. Так и есть. Но в гибком языке программирования есть серьёзная проблема с масштабированием – слишком много стилей оформления кода и способов его написания. В итоге просто не обойтись без руководств по стилю, которые помогут сориентироваться в правильной реализации.
Какое подмножество C++ или Kotlin вы используете? Что вы предпочтёте:
project.toml
или requirements.txt
? Теперь у вашего языка есть возможность поэтапной типизации с помощью аннотаций типов. Хотите ей воспользоваться? Как вы реализуете конкурентность: с помощью многопоточности, Tokio или std::async
? Чем более экспрессивный язык, тем сложнее всё становится. И здесь на сцену выходит Go. И речь не только о
gofmt
, но и о его стандартной библиотеке и согласованности. В Kotlin вам приходится гадать, что лучше использовать для ошибок: исключения или объекты Result
? В случае же Go вам всё ясно – ищем err
. Да, это многословно, но зато предсказуемо.Экспрессивные языки прекрасны, но часто создают путаницу. Вы можете использовать богатый и комплексный язык, поддерживающий миллион способов реализации одного и того же. Именно это я хочу вам показать. Как же сохранить всю эту мощь, но уменьшить беспорядок? Как избежать возникновения 500 поддиалектов? Но прежде, чем переходить к решениям, обсудим Scala.
Scala Digest. Выпуск 15
Привет, Хабр! Мы — Рома, Настя и Карина — бэкенд-разработчики Тинькофф, пишем код на Scala и горим желанием его популяризировать. Мы собираем и агрегируем новости из разных источников, включая Scala Times, блог Petr Zapletal и канал Scala Nishtyaki, добавляем новости и собственные комментарии. Свою мотивацию мы черпаем из желания развиваться и делиться полученными знаниями. Приветствуем любую обратную связь! (づ ◕‿◕ )づ
Истории
Имплиситы и тайпклассы в Scala
И мы тебя научим...
Очередная статья про имплиситы и тайпклассы в Scala, которая, в большей степени, будет интересна для начинающих скалистов.
Scala Digest. Выпуск 14
Привет, Хабр! Мы — Рома, Настя и Карина — бэкенд-разработчики Тинькофф, пишем код на Scala и горим желанием его популяризировать.
Мы собираем и агрегируем новости из разных источников, включая Scala Times, блог Petr Zapletal и канал Scala Nishtyaki, добавляем новости и собственные комментарии. Свою мотивацию черпаем из желания развиваться и делиться полученными знаниями.
Приветствуем любую обратную связь! (づ ◕‿◕ )づ
Зачем в Scala трамплины и как их использовать
В этой статье директор департамента разработки российской компании «Криптонит» и «скалист» Алексей Шуксто рассказывает о специфической технике функционального программирования, которая называется «трамплин» (trampoline).
Если кратко, то «трамплин» — это постоянный вызов в цикле новых частей вычисления вплоть до получения конечного результата. Трамплин можно рассматривать как шаблон проектирования, который позволяет избежать переполнения стека при рекурсивных вызовах функций.
Достигается это следующим образом: когда функция вызывает саму себя, то вместо этого вызова управление передаётся другой функции — трамплину. Эта функция-трамплин вызывает исходную функцию с нужными параметрами и, если нужно, передаёт управление другой функции-трамплину. Таким образом, при рекурсивных вызовах функций никакая информация не сохраняется на стеке, а управление всегда передаётся между функциями-трамплинами.
Чтобы вникнуть в детали, поясним ещё несколько моментов:
Scala Digest. Выпуск 13
Привет, Хабр! Мы — Рома, Настя и Карина — бэкенд-разработчики Тинькофф, пишем код на Scala и горим желанием его популяризировать.
Мы собираем и агрегируем новости из разных источников, включая Scala Times, блог Petr Zapletal и канал Scala Nishtyaki, добавляем дополнительные новости и собственные комментарии. Свою мотивацию черпаем из желания развиваться и делиться полученными знаниями. Приветствуем любую обратную связь! (づ ◕‿◕ )づ
Как я попробовал написать авиасимулятор
Давным-давно, в 2015 году я написал свою первую статью на хабр: Пишем простую* игровую физику самолёта
Статья появилась не сама по себе — я писал игру, но так её и не доделал. За предыдущие девять лет я несколько раз возвращался к проекту, что-то улучшал, но по-факту он так и остался на уровне прототипа.
В итоге я решил открыть исходники под MIT-лицензией, чтобы кто угодно мог их посмотреть или как-то переиспользовать: репозиторий на gitlab
Если интересно почитать о процессе, удачных и неудачных технических решениях и т.п. — читайте дальше.
Scala Digest. Выпуск 12
Привет, Хабр! Мы — Рома, Настя и Карина, и мы почти год выпускаем Scala Digest на этой площадке!
Со временем подготовка каждого выпуска стала походить на фабричное производство. У нас есть налаженный процесс: мы просматриваем материалы и оставляем комментарии. Но мы все еще горим идеей и продолжаем идти к цели, традиционно озвученной в шапке каждого выпуска: «Свою мотивацию мы черпаем из желания развиваться и делиться полученными знаниями».
Мы решили чаще делиться своим мнением, не пытаться успеть разобрать весь материал и добавить чуть больше нестандартных повествовательных приемов и мемов. Будем экспериментировать с форматом. Stay tuned!
Calypso: Схема данных MongoDB на Scala
Чтобы применять Domain-Driven Design, DDD Aggregate и Transactional outbox на MongoDB, наша команда создала open source — библиотеку calypso для работы с BSON.
Публикация для тех, кто стремится к современным практикам разработки и разделяет наше влечение к Scala 3.
Готовы к открытиям? Добро пожаловать в мир функционального программирования и надёжной работы с schema-on-read.
Внедрение зависимостей с использованием монады Cats-effect Resource
Монада Cats-effect Resource предоставляет отличную монадическую абстракцию над паттерном try-with-resource. Например, она позволяет управлять жизненным циклом зависимостей, включая закрытие/финализацию ресурса, когда он больше не нужен (закрытие соединения с базой данных, освобождение кэша при завершении работы). В сочетании с компонуемостью монад это стало очень популярным подходом для управления зависимостями — до такой степени, что такие библиотеки Scala, как http4s, предоставляют свои зависимости обернутыми в монаду Resource.
Комплексная валидация данных в Scala
Валидация данных — отнюдь не новое явление в разработке программного обеспечения. В этой статье мы с вами будем решать практическую задачу — реализуем комплексную валидацию для ситуации, когда пользователь пытается создать пароль для своего профиля.
Наша цель — аккумулировать ошибки валидации таким образом, чтобы пользователь увидел все допущенные им ошибки в пользовательском интерфейсе одновременно и мог изменить свой пароль в соответствии с нашими требования за одну попытку.
Ближайшие события
Scala Digest. Выпуск 11
Привет, Хабр! Мы — Рома, Настя и Карина — бэкенд-разработчики Тинькофф, пишем код на Scala и горим желанием его популяризировать.
Мы собираем и агрегируем новости из разных источников, включая Scala Times, блог Petr Zapletal и канал Scala Nishtyaki, добавляем дополнительные новости и cвои комментарии. Мотивацию черпаем из желания развиваться и делиться полученными знаниями. Приветствуем любую обратную связь! (づ ◕‿◕ )づ
Концепция имплицитов в Scala
Концепция implicit в Scala представляет собой одну из наиболее уникальных и мощных особенностей этого языка программирования. Этот ключевой механизм позволяет разработчикам создавать более гибкий и чистый код, улучшая читаемость и расширяемость программ.
В этой статье мы погрузимся в мир implicit в Scala, исследуем его суть, применение и возможности. Мы рассмотрим, как implicit обеспечивает поддержку для реализации различных паттернов и шаблонов программирования, а также как его использование способствует созданию более элегантных и эффективных решений задач. Давайте углубимся в эту удивительную возможность Scala и узнаем, как использовать implicit для улучшения вашего кода.
Scala: структура данных в пространстве типов — множество
Система типов Scala 3 позволяет конструировать вторичные структуры данных в пространстве типов. Ярким примером таких структур может выступать HList
, впоследствии ставший основой реализации кортежей. Кортежи в Scala 3 стали весьма гибким инструментом, позволяющим захватить в упорядоченном виде сведения о разнородных типах.
В настоящей заметке мы рассмотрим реализацию структуры "множество типов" на основе кортежей с использованием инструментов Scala 3.
Scala 3 Metaprogramming: реализация списка с известным на этапе компиляции размером
А что, если бы операции List[A].head
и List[A].tail
в Scala были бы безопасными на этапе компиляции?
В один ноябрьский вечер я задался этим вопросом, и, обладая нулевыми знаниями по метапрограммированию, принялся реализовывать список SList[A, N]
с известным на этапе компиляции размером. Даже for-comprehension в итоге получилось реализовать!
Как это получилось сделать и какими средствами языка? Если рассказывать подробно - долгая история.
Применение алгебраических типов данных для моделирования ошибок и сообщений в журнале
В функциональном программировании широко используются так называемые алгебраические типы данных. Такие данные формируются из более простых типов с использованием всего двух операций — "суммы" и "произведения". Использование таких математических операций оказывается очень удобным с точки зрения последующей обработки с помощью сопоставления с образцом ("паттерн-матчинг"/pattern matching).
Помимо удобства при разработке, математичность получающихся структур данных позволяет делать более понятные, прозрачные и логичные конструкции, находящие применение в самых разных предметных областях.
В этой заметке посмотрим на примеры моделирования ошибок и сообщений логирования.
Квитанции как способ отражения сделанной работы на уровне типов
Функциональное программирование одной из целей ставит отражение логики программы в типах входных/выходных значений функций. Типы аргументов и результатов накладывают существенные ограничения на то, как может быть реализована функция. Тем самым, позволяют делать разумные выводы о работе функции, ориентируясь только на её сигнатуру. Такое явление называется "параметричность". Замечательным примером параметричности служит такая сигнатура:
val f: [A] => A => A
Эту сигнатуру можно прочитать так: для любого типа, получив значение этого типа, вернуть какое-то значение того же типа. Исходя из того, что тип может быть любым, и никаких операций над этим типом мы не определили, единственной продуктивно завершающейся реализацией является identity
. Здесь и далее мы исключаем непродуктивные решения вида f(a) = f(a)
(зависание/отсутствие завершения) или f(a) = throw Exception()
(исключение).
Для представления эффектов часто используется конструкция IO[A]
. Значение из этого объекта можно получить, только выполнив код, содержащийся внутри. Довольно часто можно столкнуться с ситуацией, когда само значение нам не настолько интересно, как факт выполнения определённой операции. Обычно используется тип возвращаемого значения IO[Unit]
. В этой заметке предлагается воспользоваться параметричностью, чтобы получить определённые гарантии.
Что такое класс типов?
Давайте разберем, что такое класс типов. Обратимся к формальному определению:
Класс типов (type class) — это абстрактный параметризованный тип, который позволяет добавлять новое поведение к любому закрытому типу данных без использования подтипов.
Класс типов - это в первую очередь про "поведение". Когда мы определяем класс типов, то неявно заключаем "контракт", в котором описываем желаемое для определяемого класса типов поведение.
Вклад авторов
krokhmalyuk 206.0barbalion 183.1lgorSL 159.6ppopoff 149.2vuspenskiy 129.0primetalk 125.0ImLiar 125.0alextokarev 120.0IvanGolovach 120.0dos65 118.6