Size: a a a

🎄.NET Talks: Evergreen🎄

2020 March 12

DS

Doge Shibu in 🎄.NET Talks: Evergreen🎄
В scala под llvm дженерики, например, по другому делаются
источник

Dv

Dr. Friedrich von Never in 🎄.NET Talks: Evergreen🎄
Doge Shibu
В scala под llvm дженерики, например, по другому делаются
На LLVM тебе интеропиться ни с кем не нужно, это тоже послабление
источник

Dv

Dr. Friedrich von Never in 🎄.NET Talks: Evergreen🎄
Doge Shibu
Ну ок, пример из жизни напрямую. Это чуть отдельный подхода, но HKT он требует. Пусть у нас есть большой конфиг, в нём куча вложенных полей, они потенциально опциональны.

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

Вариант без HKT - мы либо делаем структуру с опциональными значениями и её версию без опциональности. Таким образом у нас дублируется достаточно большой набор структур:
// В реальности конфиги на много десятков полей суммарно
final case class Config(address : String, featureConfig: FeatureConfig, ...)
final case class FeatureConfig(...)

final case class OptionalConfig(address: Option[String], featureConfig: Option[OptionalFeatureConfig])
final case class OptionalFeatureConfig

def merge(example: Config, external: OptinalConfig): Config = ???

Либо оставляем только опциональную и нам в бизнес логике придется руками постоянно кастовать значения из нуллябельных в не нуллябельные тем или иным способом.

Вариант с HKT:
final case class Config[F[_]](address : F[String], featureConfig: F[FeatureConfig[F]], ...)

def merge(example: Config[Id], external: Config[Option]): Config[Id] = ???
Ну вот это уже дельный пример!
источник

Dv

Dr. Friedrich von Never in 🎄.NET Talks: Evergreen🎄
Всего-то надо было три часа сраться
источник

Dv

Dr. Friedrich von Never in 🎄.NET Talks: Evergreen🎄
Видать, Евангелие тебя всё-таки наставило на путь истинный
источник

Dv

Dr. Friedrich von Never in 🎄.NET Talks: Evergreen🎄
Я знал, что пригодится!
источник

DS

Doge Shibu in 🎄.NET Talks: Evergreen🎄
Dr. Friedrich von Never
Ну вот это уже дельный пример!
Т.е. это то, что у меня было в реальности. И то, что я портировал с языка с HKT в язык без HKT.

И в языке без HKT вот эта же логика занимает в разы больше кода и куда большим шансом ошибиться при этом.
источник

DS

Doge Shibu in 🎄.NET Talks: Evergreen🎄
Dr. Friedrich von Never
Ну вот это уже дельный пример!
Я его вроде бы уже показывал в фшарповой флудилке. Хотя может в растовой, уже не помню точно.
источник

DS

Doge Shibu in 🎄.NET Talks: Evergreen🎄
И сама логика мерджа в скале у меня максимально автоматизированна через shapeless и его автовывод тайпклассов.
источник

Dv

Dr. Friedrich von Never in 🎄.NET Talks: Evergreen🎄
Doge Shibu
Ну ок, пример из жизни напрямую. Это чуть отдельный подхода, но HKT он требует. Пусть у нас есть большой конфиг, в нём куча вложенных полей, они потенциально опциональны.

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

Вариант без HKT - мы либо делаем структуру с опциональными значениями и её версию без опциональности. Таким образом у нас дублируется достаточно большой набор структур:
// В реальности конфиги на много десятков полей суммарно
final case class Config(address : String, featureConfig: FeatureConfig, ...)
final case class FeatureConfig(...)

final case class OptionalConfig(address: Option[String], featureConfig: Option[OptionalFeatureConfig])
final case class OptionalFeatureConfig

def merge(example: Config, external: OptinalConfig): Config = ???

Либо оставляем только опциональную и нам в бизнес логике придется руками постоянно кастовать значения из нуллябельных в не нуллябельные тем или иным способом.

Вариант с HKT:
final case class Config[F[_]](address : F[String], featureConfig: F[FeatureConfig[F]], ...)

def merge(example: Config[Id], external: Config[Option]): Config[Id] = ???
А, нет, погоди-ка
источник

Dv

Dr. Friedrich von Never in 🎄.NET Talks: Evergreen🎄
У меня возражение.
источник

Dv

Dr. Friedrich von Never in 🎄.NET Talks: Evergreen🎄
Пока что я вижу только, что вместо типа с Id и типа с Option у тебя остался только один тип, хорошо
источник

Dv

Dr. Friedrich von Never in 🎄.NET Talks: Evergreen🎄
Но
источник

Dv

Dr. Friedrich von Never in 🎄.NET Talks: Evergreen🎄
А какой женерик код можно на этом написать?
источник

DS

Doge Shibu in 🎄.NET Talks: Evergreen🎄
Dr. Friedrich von Never
У меня возражение.
Что можно было бы мержить на этапе раньше? Где данные ещё в каком-нибудь json?

Там логика для этого сложная выходила слишком, я пробовал, всё равно не удобно.
источник

Dv

Dr. Friedrich von Never in 🎄.NET Talks: Evergreen🎄
А хотя не, тут действительно норм
источник

DS

Doge Shibu in 🎄.NET Talks: Evergreen🎄
Dr. Friedrich von Never
А какой женерик код можно на этом написать?
Суть в том, что я переиспользовал тип данных, мне не надо было его копировать два раза.
источник

DS

Doge Shibu in 🎄.NET Talks: Evergreen🎄
А там очень много вложенных в друг друга разных типов. Очень глубокий и больший конфиг
источник

Dv

Dr. Friedrich von Never in 🎄.NET Talks: Evergreen🎄
Да, этот пример всё ещё хороший.
источник

DS

Doge Shibu in 🎄.NET Talks: Evergreen🎄
Dr. Friedrich von Never
А какой женерик код можно на этом написать?
Это подход, когда мы наш тип данных так параметризуем обычно называют HKD (Higher Kinded Data), с ним достаточно много интересных трюков ещё можно делать.

Я на нём + вывод тайпклассов ещё делал штуку, которая позволяла из одного идиотского формата данных доставать сечения и аггрегации нужных мне данных без ручного написания кода.

Формат данных был вида набор потенциально гигабайтных csv в zip архиве.
final case class MinimalData[F[_]](routes: F[Route], stops: F[Stops], scheduleEntries: F[ScheduleEntry], ...)

Когда мне надо было достатать информацию о числе строк, не зачитывая их целиком, делал так:
val statistics = readData[Stats](...)
prepare(statistics)

// мог где нужно зачитать данные лениво
val lazy = readData[LazyStorage](...)
...
lazy.routes.use(rows => rows.map(...))

// где нужно сразу в эффективное хранилище
val eager = readData[EagerStorage](...)

Но это чуть более экстремальный пример, потому что там я совсем продвинутые скаловскеи фишки использовал типа path-dependent types и эмуляции match типов из дотти в некоторых местах
источник