Комментарии 20
Список переиспользует хвост, поэтому можно не бояться копирования списка.
Сопоставление с образцом не обязано разбирать какую-то структуру данных, case class или tuple, например, на составлящие. Оно может взять обычную строчку и разбить её на группы с помощью регэкспов.
Если вам не нравятся встроенные кортежи — возьмите их из shapeless, там они достаточно прокаченные.
И много, много других ньюансов. Если честно, я с трудом представляю какой должен быть формат у хабра статей, чтобы последовательно изложить язык программирования scala и сложившиеся best practise для него.
Тем более что все эти объяснения слабо помогают новичку, если он откроет существующую библиотеку и попробует догадаться как она устроена. Большая часть работы происходит во время компиляции — для этого задействуются абстрактные типы, типы высшего порядка, имплиситы. Теперь вот ещё макросы добавились. И макросы, которые производят невидимые глазу имплиситы.
Большое спасибо за комментарии.
Про Path-dependent типы я забыл, в первую очередь потому что я их очень и очень редко встречаю в коде. Не видел у новичков попытки использовать CanBuildFrom
. Скажу честно, мне никогда это в голову не приходило. Хотя, возможно я и не сталкивался с ситуациями когда это имеет смысл. Если такие ситуации есть. Рад выслушать и получить ценный опыт.
Сопоставление с образцом не обязано разбирать какую-то структуру данных, case class или tuple, например, на составлящие. Оно может взять обычную строчку и разбить её на группы с помощью регэкспов.
Технически, строчку можно рассматривать как compound тип, и Scala притворяется :)
Если вам не нравятся встроенные кортежи — возьмите их из shapeless, там они достаточно прокаченные.
Можно использовать кортежи из Shapeless, а можно подключить HListы. Если я правильно понимаю, то что первые что вторые равнозначны (при наличии имплисита в области видимости). Эти структуры данных, на мой взгляд вполне оправданы при написании каких-нибудь универсальных алгоритмов, или как они (HList) используются в Parboiled2: для представления ValueStack. В остальных случаях case classы нахожу боле применимыми: Тип именуется, поля именуются. Понятно-читаемо.
И много, много других ньюансов. Если честно, я с трудом представляю какой должен быть формат у хабра статей, чтобы последовательно изложить язык программирования scala и сложившиеся best practise для него.
Мне нравится то, что попытались сделать ребята из Twitter в их Effective Scala. И я во многом буду ссылаться на нее. Но, объять необъятное достаточно сложно.
Технически, строчку можно рассматривать как compound тип, и Scala притворяется :)Иногда приходится использовать совершенно несвязанные между собой типы в сопоставлении с образцом. Когда экстрактор не имеет соответствующего конструктора. В таком случае даже притворяться не получится.
Основной момент — это создание собственной коллекции. Новичок в скале, но опытный программист на других языках, знает множество структур данных, которых нет в стандартной библиотеке. Иногда возникает желание просто создать специфический тип на лету. Тогда и возникает вопрос, как переиспользовать обобщённые трейты коллекций из скалы.
По поводу формата статей есть идея: берётся достаточно простая задача и решается разными способами, идиотскими и заумными, простыми и сложными, эффективными и элегантными. Я вот когда захотел поплотнее разобраться с программированием на тайпклассах запилил четыре разных минималистичных реализаций HList.
Да! Даешь Скалу в массы!
Спасибо за статью, всегда с любопытсвом читаю о Скале все, что пишут на Хабре. А пишут, к сожалению, не так много, как хотелось бы :(
Мне вот вдруг стало любопытно, использование подчеркивания для неиспользуемых значений при сопоставлении шаблонам как-то оптимизируется компилятором? Сгенерируется ли при этом более эффективный код? Будет время — надо будет соорудить бенчмарк и протестить.
А еще, меня постоянно мучает вопрос: почему Скала не поддерживает именование элементов кортежа? Это ведь так удобно! Было бы гораздо читабельней видеть такое:
def someFun(): (name: String, age: Int, weight: Int)
чем, например, такое:
def someFun(): (String, Int, Int)
или вот такое:
def someFun(): SomeIntermediateReturnTypeCaseClass
В этом плане мне очень понравилось решение в Swift. И вообще в нем много удобных и уже привычных функциональных фишек, которые очень напоминали мне Scala. Будь он кросс-платформенным — так бы на нем и остался.
А еще, меня постоянно мучает вопрос: почему Скала не поддерживает именование элементов кортежа? Это ведь так удобно! Было бы гораздо читабельней видеть такое:
Я так понимаю что Мартин хотел бы чтобы мы использовали для этого case classы.
Мне вот вдруг стало любопытно, использование подчеркивания для неиспользуемых значений при сопоставлении шаблонам как-то оптимизируется компилятором? Сгенерируется ли при этом более эффективный код? Будет время — надо будет соорудить бенчмарк и протестить.
Я думаю что для начала можно посмотреть на сгенерированный scalac-ом код. Если оптимизации и происходят, то скорее всего так.
Я так понимаю что Мартин хотел бы чтобы мы использовали для этого case classы.
С ними есть несколько проблем: они не так наглядны (потому что объявлены где-то еще, а не там, где используются), приходится выделять дополнительное место для их объявления (companion object, например, или package object), их со временем накапливается очень много, и им нужно придумывать какие-то названия, которые иногда сходу бывает придумать довольно трудно (когда, например, есть несколько функций, которые возвращают кортежи, которые во многом похожи, но чуть-чуть отличаются). И это все сильно тормозит рабочий процесс.
Это как если бы в Скале не поддерживались лямбды, и все функции приходилось бы где-то объявлять и как-то называть. Несправедливо же: анонимные классы есть, анонимные функции/лямбды есть, а анонимных кортежей с именованными полями — нет.
Я думаю что для начала можно посмотреть на сгенерированный scalac-ом код. Если оптимизации и происходят, то скорее всего так.
Да, так и есть, я сначала отправил коммент, а потом об этом подумал.
def someFun() = ("name" ->> "Bob", "age" ->> 33, "weight" ->> 666).productElements
val res = someFun()
val name: String = res("name")
Код на scalafiddle.ioval updMessageOptOpt = messageOptOpt.flatMap(msg => "$mgs cruel world!")
Тут аж 2 ошибки.
- Оно даже не скомпилируется. Результат лямбды — не Option
- Интерполяция не включена
… union-типы… Scala так не умеет
Welcome to Scala.next (pre-alpha, git-hash: unknown) (OpenJDK 64-Bit Server VM, Java 1.8.0_121).
scala> type IntOrString = Int | String
defined type alias IntOrString
scala> val ios: IntOrString = if (util.Random.nextBoolean) 1 else "str"
val ios: IntOrString = 1
Это вроде как в 2.13/Dotty обещают зарелизить. Первый майлстоун уже в апреле!
Спасибо за уточнение! Добавил ссылку на ваш комментарий!
На самом-самом деле будут только абстрактные типы с ограничениями на верхнюю и нижнюю границу, а type T = X это сокращенная запись для абстрактного типа с совпадающими границами, но это нормальным людям знать не надо.
почему индексы списков в Scala начинаются с нуля, а кортежей — с единицы?В статье все описано правильно, но если очень хочется:
val isb = (666, "str", true)
val i: Int = isb.at(0)
Код целиком.Map[Username, Key] выглядит лучше, чем Map[String, String]гораздо лучше смотрится value class
class Username(val underlying: String) extends AnyVal
Советы начинающему скалисту