Pull to refresh

Comments 20

По поводу объявления типов ключевым словом type. Оно используется не только для сокращения записи, но и для объявления path-dependent types. А ещё есть анонимные типы, они же лямбды-над-типами. Хорошо хоть в стандартных коллекциях они не используются. Зато используется CanBuildFrom, о котором вы ничего не сказали. Но у любого, кто залезет в доку на обычный List или Set, возникнет вопрос — что это за зверь, как им пользоваться, как объявить с его помощью свою коллекцию?

Список переиспользует хвост, поэтому можно не бояться копирования списка.

Сопоставление с образцом не обязано разбирать какую-то структуру данных, 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 притворяется :)
Иногда приходится использовать совершенно несвязанные между собой типы в сопоставлении с образцом. Когда экстрактор не имеет соответствующего конструктора. В таком случае даже притворяться не получится.
> Не видел у новичков попытки использовать CanBuildFrom

Основной момент — это создание собственной коллекции. Новичок в скале, но опытный программист на других языках, знает множество структур данных, которых нет в стандартной библиотеке. Иногда возникает желание просто создать специфический тип на лету. Тогда и возникает вопрос, как переиспользовать обобщённые трейты коллекций из скалы.

По поводу формата статей есть идея: берётся достаточно простая задача и решается разными способами, идиотскими и заумными, простыми и сложными, эффективными и элегантными. Я вот когда захотел поплотнее разобраться с программированием на тайпклассах запилил четыре разных минималистичных реализаций 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.io
По поводу кортежей в скале. Если эта штука будет использоваться в более чем одном месте, то лучше имхо сделать case class. Иногда и для одного места лучше сделать case class. Я почти кортежами не пользуюсь.

Я ими тоже практически не пользуюсь, однако, кортеж вам может прилететь от коллег, или из какой-нибудь библиотеки.

val updMessageOptOpt = messageOptOpt.flatMap(msg => "$mgs cruel world!")

Тут аж 2 ошибки.
  1. Оно даже не скомпилируется. Результат лямбды — не Option
  2. Интерполяция не включена
Некоторые утверждения устаревают прямо на глазах:
… 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

Спасибо за уточнение! Добавил ссылку на ваш комментарий!

На самом деле в этом отношении ничего не меняется. type все еще либо path-dependent type, либо синоним. Просто появляются типы-объединения (и пересечения) и на них можно сделать синоним.

На самом-самом деле будут только абстрактные типы с ограничениями на верхнюю и нижнюю границу, а type T = X это сокращенная запись для абстрактного типа с совпадающими границами, но это нормальным людям знать не надо.
почему индексы списков в Scala начинаются с нуля, а кортежей — с единицы?
В статье все описано правильно, но если очень хочется:
val isb = (666, "str", true)
val i: Int = isb.at(0)
Код целиком.
UFO just landed and posted this here
С использованием typedef нужно быть аккуратным. По мне так вот тут
Map[Username, Key] выглядит лучше, чем Map[String, String]
гораздо лучше смотрится value class
class Username(val underlying: String) extends AnyVal
Sign up to leave a comment.

Articles