Как стать автором
Обновить
4.83

Scala *

Мультипарадигмальный язык программирования

Сначала показывать
Порог рейтинга

Посмотрите на код ниже — где в нём проблема? Пишите ваши мысли в комментариях, а ниже мы дадим решение ошибки.

object UnitError {

def printMsg(message: String): Unit = {
 println(message)
 }

def process(data: List[Int]): List[Unit] = {
 for (element <- data) yield {
 printMsg(s"Элемент: $element")
 }
 }

def main(args: Array[String]): Unit = {
 val nums = List(1, 2, 3)
 val res = process(nums)
 println(s"Результат: $res") // Вывод List[Unit] даёт неожиданный результат
 }
 }

Дальше будет решение — если не хотите спойлеров, пролистните текст ниже.

Ошибка заключается в неверной конструкции при использовании функции process. Она возвращает пустые значения: List((), (), ()). Происходит это потому, что yield собирает результаты каждой итерации. В итоге получается список, состоящий из пустых значений Unit — по одному на каждый элемент в data.

Unit в Scala — это аналог void в Java и Си-подобных языках, означающий пустое значение. В данном примере yield собирает результаты printMsg(...), которые все являются Unit (пустыми).

Программисты, привыкшие к императивным языкам и переходящие на Scala, интуитивно ожидают, что такая конструкция будет возвращать какие-то преобразованные данные, например — как это делает map в JavaScript. То есть, начинающие скалисты могут ожидать здесь на выходе список элементов, а получат пустой вывод.

Исправление

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

def printMsg(message: String): String = { // Теперь возвращает String
 println(message)
 message // Возвращает саму строку
 }

def process(data: List[Int]): List[String] = { // Теперь функция возвращает List[String]
 for {
 element <- data
 } yield {
 printMsg(s"Обрабатываем элемент: $element") // Теперь выводится результат String
 }
 }
Теги:
Рейтинг0
Комментарии1

SystemVerilog отжил свое? На пятки наступает Scala/Chisel?

DARPA, управление перспективных исследовательских проектов Минобороны США, описывает Chisel как технологию, позволяющую маленьким командам создавать большие цифровые проекты. И я вполне могу с этим согласиться, но есть нюансы.

Chisel — это, по сути, библиотека Scala, а точнее, Domain Specific Language. Языку Scala уже больше 20 лет, он постоянно развивается, сочетает функциональное и императивное программирование. При написании кода на Scala вам доступны все библиотеки Java. 

Scala — это масштабируемый язык, который позволяет добавлять свои языковые конструкции. На основе Scala можно создать язык под свои задачи. Так 12 лет назад и поступили инженеры в Беркли: выкинули из Verilog 90%, оставив только нужное, и обернули все это в Scala. Получился Chisel. 

Chisel используют прежде всего для создания RTL-описаний. Также он позволяет проводить симуляцию несложных модулей. Это удобно для создания юнит-тестов и моделирования работы различных алгоритмов. В плане симуляции не стоит возлагать на Chisel такие же надежды, как на System C или что-то подобное. Симулировать вы сможете лишь очень маленькие схемки, а генерировать — хоть целые кластеры из тысяч процессоров, вообще все, что захотите.

На основе Chisel/Scala можно написать свой HLS-инструмент (High Level Synthesis), где одним росчерком пера вы будете создавать очень большие схемы, что с использованием одного Verilog невозможно.

В блоге YADRO Денис Муратов подробно сравнил Chisel/Scala с SystemVerilog в создании RTL-описаниях, раскрыл основные преимущества и недостатки альтернативы, а также ее дополнительные возможности — функциональное программирование и переиспользование модулей.

Теги:
Всего голосов 4: ↑4 и ↓0+5
Комментарии0

Principles and Practice of Programming Languages 

Новый зверь среди академических учебников.

Выложен втихую, доступен свободно, нигде не анонсировался.

Теги:
Всего голосов 5: ↑5 и ↓0+7
Комментарии0

Сегодня я хочу выложить в открытый доступ свою библиотеку на Scala. Библиотека реализует Directed Acyclic Graph (DAG) для выполнения задач внутри одного приложения (на замену Airflow и подобных не претендую :-)) и позволяет определять задачи с зависимостями, выполнять их в правильном порядке и обрабатывать исключения, которые могут возникнуть в процессе выполнения. Библиотека писалась через призму моих личных и профессиональных потребностей, поэтому не претендует на покрытие всех возможных кейсов, встречающихся в разработке вообще.

Use case:

Иногда возникает необходимость выполнять взаимосвязанные задачи/функции/классы в рамках одного приложения, где эти задачи могут быть частично параллелизованы, то есть их можно "собрать" в DAG для более эффективного использования ресурсов и повышения общей производительности. Например при обрабтке/загрузке данных или в event-driven приложении.

Особенности:

  • Управление задачами: Добавление задач с указанными зависимостями.

  • Гибкость: Выполенение всех или только некоторых задач (с сохранением зависимостей)

  • Обработка ошибок: Встроенная обработка ошибок с передачей исключений "наверх" для упрощенного их анализа.

  • Результаты выполнения задач: Возможность получения результата выполнения задач для дальнейшего их использования программным кодом.

Код, документация и инструкция по импорту и использованию доступны на GitHub.

Буду рад любым отзывам и предложениям по улучшению. Также не стесняйтесь задавать вопросы и заводить issue :-)

Теги:
Рейтинг0
Комментарии0

Привет! Сегодня поделюсь с вами рассказом, почему Scala-макросы могут замедлять CI не только временем компиляции. Выход из такой ситуации мы нашли не сразу, но решение оказалось настолько удачным, что наша команда решила им поделиться со всем сообществом. А дело было так…

У нас есть монорепозиторий на 4 млн LOC Scala-кода, мы собираем его в Bazel с кешированием результатов сборки, чтобы разработчики не ждали компиляцию и тестирование кода, который они не трогали. Долгое время у нас болело, что чужие тесты иногда запускались на CI.

Стали разбираться и выяснили: не весь наш код компилируется идемпотентно. Повторная компиляция одного и того же Scala-кода для многих таргетов создаёт jar-архивы с разной хэш-суммой, но семантически одинаковым содержанием. И весь зависящий от них код собирается заново. В этом виноваты Scala-макросы: при повторной компиляции кода с макросом, генерирующим sealed-иерархию, порядок перечисления наследников в байткоде может отличаться от предыдущей компиляции. Такое поведение мы обнаружили в библиотеках chimney и play-json.

То есть компиляция кода с использованием макросов из этих библиотек работала не идемпотентно и ломала кеширование сборок. Аналогичное поведение мы нашли и в одном макросе для ZLayer.

Мы сделали эти макросы детерминированными:

Если вы используете кеширование сборок и эти библиотеки — обновитесь до последних версий, чтобы применить эти изменения.

Теги:
Всего голосов 5: ↑5 и ↓0+10
Комментарии0

Выкатываем лето на прод в Казани!

Наш летний ИТ-фестиваль «Сезон кода» проведем 13 июля. Будем делиться опытом, говорить про технологии, танцевать и уже по доброй традиции помогать «Семейному дому» в Казани.

Сезон кода: ИТ-фест в Казани
Сезон кода: ИТ-фест в Казани

Что по программе:

— доклады по Java, Scala, QA, Mobile и Data от инженеров Т-Банка, Сбера, VK и Магнит Маркет;
— квиз и кастомные настолки, чтобы поиграть в перерывах;
— спорт-, лаундж- и фотозоны, где можно размяться, отдохнуть и сделать пару снимков на память.

Стать участником ИТ-феста просто: нужно зарегистрироваться и внести пожертвование от 1000 ₽. Подробности на этой странице.

Лето, код, комьюнити 💛

#сезон_кода

Теги:
Всего голосов 1: ↑1 и ↓0+3
Комментарии0

В scala 3 есть свои context receivers и они не похожи на то что есть в Kotlin!

object PostConditions:
  opaque type WrappedResult[T] = T

  def result[T](using r: WrappedResult[T]): T = r

  extension [T](x: T)
    def ensuring(condition: WrappedResult[T] ?=> Boolean): T =
      assert(condition(using x))
      x
end PostConditions
import PostConditions.{ensuring, result}

val s = List(1, 2, 3).sum.ensuring(result == 6)

Разберу по строчкам:

opaque type WrappedResult[T] = T: непрозрачный тип. Внутри объекта PostConditions компилятор "знает" что это один и тот же тип, снаружи смотрит на них как на два разных типа. Это не даст перепутать тип с типом обёртки, и, например, случайно присвоить одно в другое.

def result[T](using r: WrappedResult[T]): T = r: функция, которая принимает неявный параметр откуда-то из контекста и возвращает его как нормальное значение.

extension [T](x: T):
def ensuring(condition: WrappedResult[T] ?=> Boolean): T = ....
extension метод, который принимает лямбду. Лямбда принимает неявный параметр, а метод ensuring его предоставляет.

val s = List(1, 2, 3).sum.ensuring(result == 6)Метод списка sum возвращает Int, для него вызывается вышенаписанный метод ensuring, в который передаётся лямба. внутри лямбды есть неявный параметр и потому там (и только там, больше нигде) можно вызвать функцию result, которая вернёт этот самый T.

В коде где-либо снаружи PostConditions объект типа WrappedResult[T] не получится создать.

Теги:
Всего голосов 4: ↑4 и ↓0+4
Комментарии0