Русский
Русский
English
Статистика
Реклама

F

Две недели с F

06.03.2021 16:12:44 | Автор: admin


А вы когда-нибудь записывали свои впечатления от изучения нового языка? Записывали все, что вам не понравилось, чтобы через пару недель изучения понять, насколько недальновидными и тупыми они были?

КАРТИНКА ДО КАТА

На днях я понял F#, и попытаюсь описать словами мысль, стоящую за языком.

Почему ты не Powershell?


Первым делом, как только уселся за F#, ознакомившись со стайл гайдом, начал переносить команды из Powershell, которые использую чаще всего. В языке есть пайп оператор, ну, можно программировать как на Powershell. Да?

Вот сейчас возьму и функционально получу корневую директорию файла, не используя богомерзкие классы классы из .net.

Все очень просто, берем путь, преобразовываем строку в массив помощью split, по System.IO.Path.DirectorySeparatorChar, берем последний элемент из массива и делаем .trim.

Да, на F# есть весь .net а в .net всё есть, но я не за этим сел. Вот так этот велосипед выглядит на Powershell:

$Path=C:\users\test\folder$Trimer=$Path.Split(\)[$Path.Split(\).Count-1]$Path.Trim($Trimer)

В этом коде много проблем, он просто ужасен, но именно его я и буду переписывать.

Сейчас просто перепишу, ну что может пойти не так?

Не такой уж и умный компилятор


let splitPath inputObject: string =    let q = inputObject.Split(System.IO.Path.DirectorySeparatorChar)     q

Написав две строки кода, сразу получаю ошибку:

FS0072 Поиск объекта неопределенного типа, основанного на информации до данной точки программы. Возможно, перед данной точкой программы потребуется аннотация типа, с целью ограничения типа объекта. Возможно, это позволит разрешить поиск.

На разборку у меня ушло минут 30, я не мог поверить, оказывается, компилятор не смог определить, что имеет дело со строкой из типа входного объекта.

let splitPath inputObject: string =    let mutable inputObject : string = inputObject    let q = inputObject.Split(System.IO.Path.DirectorySeparatorChar)     q

Пришлось задавать типы прямо внутри функции лишний раз копируя входные данные.

LInq


Компилятор не делает всю работу за меня ну и ладно. Такие сложности не остановят меня от написания своего собственного костыля.

Часть моей гениальной задумки лежала на Linq, на Trim и Last. Но Trim не работает со string, он работает c Char, то есть нужно переворачивать последний элемент листа и откусывать от строки по символу.

Нет ++


Linq работает не так, как я хочу ну и не надо. Я посчитаю количество элементов в массиве и выберу нужный, а потом переверну его, разобью на char[] и обрежу таки стрингу!

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

На этом месте я понял, что совсем ничего не понимаю и начал изучать язык.

F#, ну зачем?


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

Printf, printfn, нейминг


Это покоробило меня еще в самом начале, функция printf выводит символы в той же строке, а printfn в новой строке. В этом весь F#.

Меня, как человека знакомого с концепцией функционального программирования из Powershell это покоробило, после Powershella любой другой язык кажется каким-то куцым.

Если бы я делал F#, я бы сделал какую-то такую функцию:

Out-HostInputstring -Newline

Подход божественного павершелла к аргументам лучший, ибо не нужно держать в голове порядок указания параметров.

Napespaces и ленивый Open


Сразу после своего собственного костыля я попытался сделать сайт на основе шаблона ASP NET MVC. К сожалению, из MVC там только С, но контроллеры действительно получаются очень красивые и компактные.

В F# все файлы в F# ведут себя как скрипты. Переменная или функция не объявленные выше не могут использоваться ниже.

С помощью директивы Open мы открываем неймспейсы и модули.Это аналог Using и Import-Module. По аналогии с Powershell, я могу импортнуть файл в котором есть коллекция со всеми её функциями, вставить её в середину файла и все заработает прям как в павершелле? Нет.

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

||>, <|, почему не | ?


Оператор |> нужен чтобы передавать значение в функцию.

||> существует чтобы передавать кортежи в функцию.

|||> а этот монстр передает кортеж из трёх в функцию.

Работа с кортежами выглядит так:

(1,2)||>someFunction

А с единичной переменной вот так:

1|>someFunction

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

Эта ошибка была совершена из-за другой ошибки, <| Pipe back оператора. Он был введен, чтобы при композиции в некоторых случаях можно было избавиться от скобочек. К примеру это:

printfn%s(stringValue)

Можно написать так:

printfn%s<|stringValue

Дон Сайм, архитектор языка как раз говорил об этом тут.

Почему ты не F#?


Помните о лекциях от крутых мужиков? Я прослушал лекцию от Скотта Влашина и на этом моменте меня пробило, я понял эту гигантскую мысль, осознание накрыло со всех сторон, это совсем другая парадигма. Я мог только сидеть на стуле и ухать.

Some, None


Скажем, мы пытаемся прочитать файл на двух языках. В F# и С#. К примеру, пытаемся прочитать txt файл и что-то сделать с его содержимым. Если что-то пойдет не так, код написанный на C# упадет сразу в двух местах, потому что StreamReader не может прочитать файл, которого нет, да и обработчик не умеет работать с нулём.

Вся задумка состоит в том, что даже если мы не возвращаем Value, мы всегда возвращаем что-то, у нас есть тип, у нас есть Value of None. И если мы не получили Some of Value, то получили None.

Как пример, работа с дотнетовскими коллекциями в F#:

  let dictionary = Dictionary<string, string> ()       let getFromDictionary key =        match dictionary.TryGetValue (key) with        | true, value -> Some (value)        | false, _ -> None

Кстати, этот же метод можно реализовать и на C# с помощью расширений, например для этого есть LanguageExt.Core и Maybe монады, но на C# все это выглядит просто ужасно.

Discriminated union aka алгебраические типы


Чтобы прочитать файл на C# мы должны писать защитный код как минимум в 2 местах. Сначала мы должны проверить, что файл существует и что файл соответствует формату, чтобы не упал streamreader.

Чтобы не падал наш процессор, нужно проверить, что файл не пустой и что он тоже правильного формата. Это абсолютно легитимный способ писать код на C#, но не на F#.

К примеру, возьмем пример, где наша программа может работать только с txt и ini файлами.

type ValidInput =    | Txt of string    | Ini of string type InvalidInput =    | WrongFormat    | FileDoesNotExists    | FileIsEmpty    | OtherBadFile type Input =    | ValidInput of ValidInput    | InvalidInput of InvalidInput

На F# защитный код пишется только в самом начале. Благодаря мощной системе типов и паттерн матчингу мы можем хендлить все варианты развития событий, не смешивая защитную логику с остальной.

let input = testInputObject request match input with| ValidInput (x) -> invokeAction x | InvalidInput (x) -> writeReject x 

Непробиваемый дизайн языка


Непробиваемый ни нулями, ни багами. И гениальность состоит из нескольких компонентов:

  • Нет return. Вернуть значение из функции можно только в конце после отработки всей логики.
  • Нет if без else. Потому, что if без else обычно применяется там, где будет возвращен null.
  • Type of Value. В F# всегда возвращается либо тип, либо значение какого-то типа, но никогда не Null.

Всего 3 принципа которые даже я понял. Всего три принципа были нужны, чтобы отлавливать баги на стадии компиляции.

Вся область проектирование перед глазами


Это вытекает из особенности языка, все файлы в F# ведут себя как скрипты. Переменная или функция не объявленные выше не могут использоваться ниже.

Что с одной стороны, это не дает писать код в вольной спагетти манере, но с другой, становится ясно, куда смотреть. Если функция используется ниже, то она объявлена выше.

Особенно прекрасно это смотрится на бизнес-логике связанной с ASP .NET. Все типы и все функции, связанные с определенной страницей на сайте все на одном листе.

Имутабельность по умолчанию


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

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

DDD


Domain Driven Design в F# это абсолютно нативная вещь и пожалуй, лучший способ разработки. Если вы начнете писать на F#, то сможете и не заподозрить, что начали так делать.

Скажем, мы храним в базе данных данные о пользователях, где часть из них кошки, а другая попугаи и нам нужно понять, с кем мы имеем дело. У пользователя есть поле с его ID и булёвое поле HaveWings.

То вот это не F# и не DDD:

let getUserType key =    let user = getFromDatabase key    if user.HaveWings = true then "Parrot"    else "Cat"

В этом случае мы не используем паттерн матчинг, что делает его нерасширяемым и мы не используем типы, поэтому компилятор нам больше не помощник.

А это уже и F# и DDD:

let getUserType key =    let user = getFromDatabase key    if user.HaveWings = true then "Parrot"    else "Cat"

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

Плюс мы используем паттерн матчинг, что в будущем, когда модель данных станет сложнее, позволит нам избавиться от вложенных else if и длинных свитчей.

В целом, можно программировать на F# и без DDD, но если можно сделать код человекопонятным, пот почему бы и нет?



Все то время, что я не знал, что пытался писать на смеси C# и Powershell даже не понимая того, что в F# то, как ты пишешь код так же важно, как и соблюдать синтаксис.

Я понял в чем суть имутабельности по дефолту, я понял DDD, я понял, в чем главная задумка языка.

Так я полюбил F# и мне больше не бомбит.

Скрытый текст
Монстр-велосипед был добавлен в статью в юмористических целях, но я таки его доделал. И в нем не меньше (если не больше) проблем, чем в коде выше, но тем не менее.

Если знаете, как сделать его еще лучше свисните.

let splitPath inputObject =     let mutable inputObject : string = inputObject    let stringArray = inputObject.Split(System.IO.Path.DirectorySeparatorChar)       let mutable outString = ""    for i in stringArray do        outString <- i      let chararray = outString |> Seq.toList |> List.rev    for c in chararray do        inputObject <- inputObject.TrimEnd(c)     printfn "%s" inputObject splitPath @"C:\users\test\folder"


Подробнее..

Завязывайте со своим хабр не торт. Хабр это феномен

04.08.2020 20:06:34 | Автор: admin


Когда я работал в офисе, я не очень понимал, зачем мне нужен хабр. Все больные для разрабов темы мы обсуждали в курилке, все сложные технические проблемы я изучал по доке, а ответы на простые вопросы искал на стековерфлоу. Когда я стал удаленщиком, ежедневное чтение хабра стало важным ритуалом. В офисе вокруг меня было две сотни разрабов, а тут только трое коллег, с которыми не особо и охота созваниваться.

Но потребность знать, что вообще происходит в индустрии есть. Хочется знать, что происходит, кого и что волнует, что люди думают о новых технологиях, и какой вообще сейчас дух у сообщества. Вот здесь Хабр и заменил мне тусовку в офисе. И новости, и исследования, и мнения, и сотни комментариев от коллег по цеху.

Статья, которая висит в топе за месяц обычно центр айтишной повестки в стране. Когда пиндосы в очередной раз заставляют всех переименовывать ветки я иду за пивом. Что бы вечерком зайти в самую популярную статью, и почитать, что думают об этом ребята с хабра.

То, что многие говорят в офисе, дома мне было сказать некому я стал писать на Хабр сам и говорить об этих вещах сразу всем. И оно сработало! Меня много читают. Обсуждают, критикуют все как в офисе. Так хабр стал крупной частью моей жизни. Теперь я всегда или думаю над новой статьей, или собираю фидбек со старой. Даже работу я стал рассматривать как источник новых мыслей и проблем все для новых статей.

Хабр стал для меня такой дефолтной вкладкой, которая открыта вообще всегда, первый сайт, который я открываю утром, и последний, который я закрываю вечером. И мне казалось, что именно так это и должно работать. Гигантское сообщество, куча читателей, и все из одной индустрии что может быть круче?



Недавно я понял, что очень многие считают иначе. Хабр это не всё снгшное айти. Есть ещё люди из конференций, люди из твиттера, люди со своими площадками да много кого. Так вот, у них в целом о хабре мнение простое помойка.

Аргументы тоже простые. Закрытое, токсичное комьюнити со своей атмосферой, где выдавливают инакомыслие, чморят новичков, и никого не принимают в свой клуб. Рассадник корпоративных блогов, которые занимаются переводами низкосортных материалов с американских технобложиков. Пристанище дурачков, которые не умеют читать по английски, и сидят в своем болоте, высмеивая вещи, которых не понимают.

Я капец как не согласен. Плохих вещей тут хватает корпоративные блоги, по большей части, и правда изо дня в день переводят недостатьи про про какие-нибудь js-промисы. В комментах бывает жарковато, и если ворвешься сюда с пикабушной лексикой, утонешь в минусах. Каждый хороший и интересный человек, которому есть что сказать, тоже столкнется с парой трудностей, до того как будет услышан.

Но черт возьми, оно того стоит! Хабр читает миллионов десять человек. Десять чертовых миллионов, которые друг друга понимают. И они все из IT. Вот ты придумал новый фреймворк, написал про него в комментариях будет тысяча экспертов по этой теме. И они понапишут в комментах столько ценного, что из этого можно будет собрать новую публикацию.

По своему авторскому опыту скажу люди тебя не просто услышат. Если описываешь проблему они придут к тебе в телегу, чтобы помочь. Это тот редкий случай, когда всем действительно не насрать. Я, маленький, ничего не умеющий лох из Иванова, и я просто рассказываю про свои проблемы а получаю сотни человек, у которых есть или были такие же проблемы и они приходят ко мне, чтобы это обсудить. Для меня это очень ценно.

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

Бывает обидно, что сложные технические статьи почти никогда не будут висеть на главной но они и не должны. Двести тысяч человек не будут обсуждать новую фичу из 9-го сишарпа, потому что она их не касается и это абсолютно нормально. Если ты написал статью про сишарп и тебя прочитало пятнадцать тысяч человек это просто отлично. Больше нигде на русском тебя столько людей не прочитает.

Ты, скорее всего, решил настоящую проблему настоящих инженеров, сделал очень полезную штуку. И тебе не надо соревноваться в рейтингах с чуваками навроде меня я то не решаю проблемы, я просто помогаю людям хорошо провести время.

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

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

И вот на хабре находится место и хорошее место и для рабочих статей, и для расслабона.

При этом и говном тебя тут тоже могут неплохо обкидать. У меня есть несколько статей, которые получили больше сотни минусов огромное число. Мне повезло, что единомышленников больше. Но факт есть факт куча людей предпочли бы, чтобы меня здесь вообще нахер не было. Меня сильно беспокоит какая-то проблема, я её вынашиваю, готовлю публикацию а в ответ получаю ярлык фил опять набрасывает. И мне от этого достаточно тяжело. Критика дается мне ой как нелегко. У меня даже есть правило не отвечать под комментариями в своих статьях, пока я хоть чуть-чуть на взводе.

Но таковы правила игры. Пусть лучше меня прочитает сто тысяч человек, сто из них назовет мудаком, но и положительный фидбек я тоже получу. Я ведь обычный лох из провинции, так? Для меня офигенно важна идея, что то, чем я занимаюсь, мои мысли и проблемы чего-то стоят. Я бы очень хотел, чтобы люди, которых это бесит просто не видели моих статей. Но так не получится, и я доволен тем, что есть.

Я стал намного спокойнее переносить критику на Хабре после того, как написал статью на vc.ru там я буквально утонул в дерьме. При том, что статья была заплюсована. Есть сайты вообще без модерации, и они обречены их наводнят люди, которые принесли хаос поток необоснованных оскорблений и ненависти. Есть модель, когда все модерируется сотрудниками компании и это тоже так себе вечный источник срачей про несправедливость.

Когда все решает народ тоже всякое может быть. Но читатели и авторы хабра не рандомные мудаки, и хабр доверил нам самим модерировать и формировать сообщество. Это, как мне кажется, самый лучший вариант. Частная дискриминация, очень простая и правильная мысль ты нам не нравишься, и мы тебя не хотим слушать. Мы не запрещаем тебе разговаривать, но мы запрещаем тебе заставлять нас тебя слушать. Иди и говори что хочешь, но не у нас дома.

У такой системы есть сбои не раз видел, как человека сровняли с землей, потому что не просекли сарказм. Но человек заведет новый аккаунт, а у нас есть гарантия, что сюда не придёт толпа необразованных идиотов, и не смешает нам все с дерьмом.



У меня много публикаций с очень большим рейтингом, больше, чем у большинства из вас. И мой секрет не в том, что мой друг редактор делает из них конфетки. И не в том, что я специально набрасываю на огненные темы. Все гораздо проще. Если ты разраб, и тебя что-то очень сильно беспокоит есть хороший шанс, что ты такой не один. Я абсолютно уверен, что у большинстваа здесь есть, что рассказать остальным и они это сделают, рано или поздно. Потому что в этом то и суть хабра. Это место, где инженеры читают инженеров. И хабр такой один.

Когда я объяснял тоже самое хейтерам хабра, у них оставался только один аргумент хабр, это снгшное болото. У нас тут все ненастоящее, мы просто нахваливаем друг дружку, а на деле, все эти наши проблемы цивилизованный мир давно уже обсудил и отбросил, Таким людям я швыряю в лицо свою статью про код-ревью, которая шикарно зашла на хабре, и ещё лучше зашла на реддите. Нет, мужики. Если у тебя есть большая проблема, которая ещё не решена, и её горячо обсуждают на хабре можешь быть уверен она волнует всех не только в снг.

Мы тут позвали к себе в подкаст Вастрика разраба, который писать научился. Причем настолько хорошо, чтобы взять, да и поднять свой собственный блог, который читает много людей. Он тоже из тех, кто сильно не любит хабр, и мы хорошенько об этом поговорили.

Я из этого сделал простой вывод Вася достаточно хорош, чтобы жить и высказываться без хабра. Но таких немного. Имидж хабра в глазах людей извне имеет для нас огромное значение, и нам стоит, где можем, пресекать эту риторику в духе хабр уже не тот, хабр неправильный, хабр бесполезный.

Хабр феномен. И это очень хороший феномен.
Смотрите мой подкаст
Подробнее..

Из песочницы Асинхронность в C и F. Подводные камни асинхронности в C

18.07.2020 16:04:37 | Автор: admin
Привет, Хабр! Представляю вашему вниманию перевод статьи Async in C# and F# Asynchronous gotchas in C# автора Tomas Petricek.

Еще в феврале я присутствовал на ежегодном саммите MVP мероприятии, организованном Microsoft для MVP. Я воспользовался этой возможностью, чтобы посетить также Бостон и Нью-Йорк, сделать два выступления про F# и записать лекцию Channel 9 о провайдерах типов. Несмотря на другие мероприятия (такие как посещения пабов, общение с другими людьми про F# и долгий сон по утрам), мне также удалось провести несколько обсуждений.

image

Одним обсуждением (из тех, что не под NDA) была беседа Async Clinic о новых ключевых словах в C# 5.0 async и await. Люциан (Lucian) и Стивен (Stephen) говорили о распространенных проблемах, с которыми сталкиваются разработчики C# при написании асинхронных программ. В этом посте я рассмотрю некоторые проблемы с точки зрения F#. Разговор был довольно оживленным, и кто-то описал реакцию аудитории F # следующим образом:

image
(Когда MVP, пишущие на F#, видят примеры кода C#, они хихикают, как девочки)

Почему так происходит? Оказывается, что многие из распространенных ошибок невозможны (или гораздо менее вероятны) при использовании асинхронной модели F# (которая появилась в версии F# 1.9.2.7, выпущенной в 2007 году и поставлявшейся с Visual Studio 2008).

Подводный камень #1: Async не работает асинхронно


Давайте сразу перейдем к первому сложному аспекту модели асинхронного программирования на C #. Посмотрите на следующий пример и попытайтесь представить, в каком порядке будут напечатаны строки (я не смог найти точный код, показанный на выступлении, но я помню, как Люциан демонстрировал нечто подобное):

  async Task WorkThenWait()  {      Thread.Sleep(1000);      Console.WriteLine("work");      await Task.Delay(1000);  }   void Demo()   {      var child = WorkThenWait();      Console.WriteLine("started");      child.Wait();      Console.WriteLine("completed");  }

Если вы думаете, что будет напечатано started, work и completed, вы ошибаетесь. Код печатает work, started и completed, попробуйте сами! Автор хотел начать работу (вызвав WorkThenWait), а затем дождаться выполнения задачи. Проблема в том, что WorkThenWait начинается с выполнения каких-либо тяжелых вычислений (здесь Thread.Sleep), и только после этого использует await.

В C# первая часть кода в async-методе выполняется синхронно (в потоке вызывающей стороны). Вы можете исправить это, например, добавив в начале await Task.Yield().

Соответствующий код F#


В F# это не проблема. При написании асинхронного кода на F# весь код внутри блока async { }отложен и запускается позже (когда вы явно запускаете его). Приведенный выше код C# соответствует следующему в F#:

let workThenWait() =     Thread.Sleep(1000)    printfn "work done"    async { do! Async.Sleep(1000) } let demo() =     let work = workThenWait() |> Async.StartAsTask    printfn "started"    work.Wait()    printfn "completed"  

Очевидно, что функция workThenWait не выполняет работу ( Thread.Sleep) как часть асинхронных вычислений, и что она будет выполняться при вызове функции (а не при запуске асинхронного рабочего процесса). Обычным шаблоном в F# является обёртывание всего тела функции в async. В F# вы бы написали следующее, что и работает, как ожидалось:

let workThenWait() = async{     Thread.Sleep(1000)    printfn "work done"    do! Async.Sleep(1000) }  

Подводный камень #2: Игнорирование результатов


Вот еще одна проблема в модели асинхронного программирования на C# (эта статья взята непосредственно из слайдов Люциана). Угадайте, что произойдёт, когда вы запустите следующий асинхронный метод:

async Task Handler() {   Console.WriteLine("Before");   Task.Delay(1000);   Console.WriteLine("After");} 

Вы ожидаете, что он напечатает Before, подождёт 1 секунду, а затем напечатает After? Неправильно! Будут напечатаны оба сообщения сразу, без промежуточной задержки. Проблема состоит в том, что Task.Delay возвращает Task, а мы забыли подождать, пока она не завершится (используя await).

Соответствующий код F#


Опять-таки, вероятно, вы не столкнулись бы с этим в F#. Вы вполне можете написать код, который вызывает Async.Sleep и игнорирует возвращаемый Async:

let handler() = async{    printfn "Before"    Async.Sleep(1000)    printfn "After" } 

Если вы вставите этот код в Visual Studio, MonoDevelop или Try F #, вы тут же получите предупреждение:

warning FS0020: This expression should have type unit, but has type Asyncunit. Use ignore to discard the result of the expression, or let to bind the result to a name.

предупреждение FS0020: Это выражение должно иметь тип unit, но имеет тип Asyncunit. Используйте, ignore, чтобы отбросить результат выражения или let, чтобы связать результат с именем.


Вы по-прежнему можете скомпилировать код и запустить его, но, если вы прочитаете предупреждение, то увидите, что выражение возвращает Async и вам нужно дождаться его результата, используя do!:

let handler() = async {   printfn "Before"   do! Async.Sleep(1000)   printfn "After" } 

Подводный камень #3: Асинхронные методы, которые возвращают void


Довольно много времени в разговоре было посвящено асинхронным void-методам. Если вы пишете async void Foo() { }, то компилятор C# генерирует метод, который возвращает void. Но под капотом он создает и запускает задачу. Это означает, что вы не можете предугадать, когда работа действительно будет выполнена.

В выступлении прозвучала такая рекомендация по использованию шаблона async void:

image
(Ради всего святого, прекратите использовать async void!)

Справедливости ради, нужно заметить, что асинхронные void-методы могут быть полезны при написании обработчиков событий. Обработчики событий должны возвращать void, и они часто начинают некоторую работу, которая продолжается в фоновом режиме. Но я не думаю, что это действительно полезно в мире MVVM, (хотя, безусловно, делает хорошие демо на конференциях).

Позвольте мне продемонстрировать проблему с помощью фрагмента из статьи MSDN Magazine об асинхронном программировании на C#:

async void ThrowExceptionAsync() {    throw new InvalidOperationException();}public void CallThrowExceptionAsync() {    try     {        ThrowExceptionAsync();    }     catch (Exception)     {        Console.WriteLine("Failed");    }} 

Думаете, этот код напечатает Failed? Я надеюсь, вы уже поняли стиль этой статьи
Действительно, исключение не будет обработано, поскольку после запуска работы происходит немедленный выход из ThrowExceptionAsync, а исключение будет возбуждено где-то в фоновом потоке.

Соответствующий код F#


Так что, если вам не нужно использовать функции языка программирования, то, вероятно, лучше не включать эту функцию в первую очередь. F# не позволяет вам писать функции async void если вы переносите тело функции в блок async { }, тип возвращаемого значения будет Async. Если вы используете аннотации типов и требуете unit, вы получите несоответствие типов (type mismatch).

Вы можете написать код, который соответствует вышеупомянутому коду C#, используя Async.Start:

let throwExceptionAsync() = async {    raise <| new InvalidOperationException()  }let callThrowExceptionAsync() =   try     throwExceptionAsync()     |> Async.Start   with e ->     printfn "Failed"

Здесь исключение также не будет обработано. Но происходящее более очевидно, потому что мы должны написать Async.Start явно. Если мы этого не сделаем, мы получим предупреждение о том, что функция возвращает Async и мы игнорируем результат (так же, как в предыдущем разделе Игнорирование результатов).

Подводный камень #4: Асинхронные лямбда-функции, которые возвращают void


Ситуация ещё более усложняется, когда вы передаете асинхронную лямбда-функцию какому-либо методу в качестве делегата. В этом случае компилятор C # выводит тип метода из типа делегата. Если вы используете делегат Action (или аналогичный), то компилятор создает асинхронную void-функцию, которая запускает работу и возвращает void. Если вы используете делегат Func, компилятор генерирует функцию, которая возвращает Task.

Вот образец из слайдов Люциана. Когда завершится следующий (совершенно корректный) код через одну секунду (после того, как все задачи завершили ожидание) или немедленно?

Parallel.For(0, 10, async i => {    await Task.Delay(1000);});

Вы не сможете ответить на этот вопрос, если вы не знаете, что для For есть только такие перегрузки, которые принимают делегаты Action и, таким образом, лямбда-функция всегда будет компилироваться как async void. Это также означает, что добавление какой-то (возможно, полезной) нагрузки будет ломающим изменением (breaking change).

Соответствующий код F#


Язык F# не имеет специальных асинхронных лямбда-функций, но вы вполне можете написать лямбда-функцию, которая возвращает асинхронные вычисления. Такая функция будет возвращать тип Async, поэтому она не может быть передана в качестве аргумента методам, которые ожидают возвращающий void делегат. Следующий код не компилируется:

Parallel.For(0, 10, fun i -> async {  do! Async.Sleep(1000) })

Сообщение об ошибке просто говорит о том, что тип функции int -> Asyncне совместим с делегатом Action(в F# должно быть int -> unit):

error FS0041: No overloads match for method For. The available overloads are shown below (or in the Error List window).

ошибка FS0041: не найдены перегрузки для метода For. Доступные перегрузки показаны ниже (или в окне списка ошибок).


Чтобы получить то же поведение, что и в приведенном выше коде C#, мы должны явно начать работу. Если вы хотите запустить асинхронную последовательность в фоновом режиме, это легко делается с помощью Async.Start (который принимает асинхронное вычисление, возвращающее unit, планирует его и возвращает unit):

Parallel.For(0, 10, fun i -> Async.Start(async {  do! Async.Sleep(1000) }))

Вы, конечно, можете написать это, но увидеть, что происходит, довольно легко. Также нетрудно заметить, что мы тратим ресурсы впустую, так как особенность Parallel.For в том, что он выполняет вычисления с интенсивным использованием процессора (которые обычно являются синхронными функциями) параллельно.

Подводный камень #5: Вложенность задач


Я думаю, что Лукиан включил этот камень просто чтобы проверить умственные способности людей в аудитории, но вот он. Вопрос в том, подождёт ли следующий код 1 секунду между двумя выводами на консоль?

Console.WriteLine("Before");await Task.Factory.StartNew(    async () => { await Task.Delay(1000); });Console.WriteLine("After");

Совершенно неожиданно, но между этими выводами нет задержки. Как это возможно? Метод StartNew принимает делегат и возвращает Task где T тип, возвращаемый делегатом. В нашем случае делегат возвращает Task, поэтому в результате мы получаем Task. await ожидает только завершения внешней задачи (которая немедленно возвращает внутреннюю задачу), при этом внутренняя задача игнорируется.

В C# это можно исправить, используя Task.Run вместо StartNew (или удалив async/await в лямбда-функции).

Можно ли написать что-то подобное в F #? Мы можем создать задачу, которая будет возвращать Async, используя функцию Task.Factory.StartNew и лямбда-функцию, которая возвращает асинхронный блок. Чтобы дождаться выполнения задачи, нам нужно будет преобразовать ее в асинхронное выполнение, используя Async.AwaitTask. Это означает, что мы получим Async<Async>:

async {  do! Task.Factory.StartNew(fun () -> async {     do! Async.Sleep(1000) }) |> Async.AwaitTask }

Опять-таки, этот код не компилируется. Проблема в том, что ключевое слово do! требует с правой стороны Async, но в действительности получает Async<Async>. Другими словами, мы не можем просто игнорировать результат. Нам нужно что-то с этим сделать явно
(для воспроизведения поведения C# можно использовать Async.Ignore). Сообщение об ошибке, возможно, не такое понятное, как предыдущие, но даёт общее представление:

error FS0001: This expression was expected to have type Asyncunit but here has type unit

ошибка FS0001: Ожидается выражение типа Asyncunit, присутствует тип unit

Подводный камень #6: Асинхронность не работает


Вот еще один проблемный фрагмент кода со слайда Люциана. На этот раз проблема довольно проста. Следующий фрагмент определяет асинхронный метод FooAsync и вызывает его из Handler, но код не выполняется асинхронно:

async Task FooAsync() {    await Task.Delay(1000);}void Handler() {    FooAsync().Wait();}

Определить проблему несложно мы вызываем FooAsync().Wait(). Это означает, что мы создаем задачу, а затем, используя Wait, блокируем программу до её завершения. Проблему решает простое удаление Wait, потому что мы просто хотим запустить задачу.

Этот же код можно написать на F#, но асинхронные рабочие процессы не используют задачи .NET (изначально предназначенные для вычислений с привязкой к ЦП), а вместо этого используют тип F# Async, который не укомплектован Wait. Это означает, что вы должны написать:

let fooAsync() = async {    do! Async.Sleep(1000) }let handler() =     fooAsync() |> Async.RunSynchronously

Конечно, такой код можно написать и случайно, но, если вы столкнулись с проблемой неработающей асинхронности , вы легко заметите, что код вызывает RunSynchronously, поэтому работа выполняется как и следует из названия синхронно .

Резюме


В этой статье я рассмотрел шесть случаев, в которых модель асинхронного программирования в C# ведёт себя неожиданным образом. Большинство из них основавыются на беседе Люциана и Стивена на саммите MVP, поэтому спасибо им обоим за интересный список распространённых ловушек!

Для F# я пытался найти ближайшие соответствующие фрагменты кода, используя асинхронные рабочие процессы. В большинстве случаев компилятор F# выдает предупреждение или ошибку либо модель программирования не имеет (прямого) способа выразить тот же код. Я думаю, это подтверждает утверждение, которое я сделал в предыдущем посте блога: модель программирования F# определенно кажется более подходящей для функциональных (декларативных) языков программирования. Я также думаю, что она облегчает рассуждения о том, что происходит.

Наконец, эту статью не следует понимать как разрушительную критику асинхронности в C# :-). Я полностью понимаю, почему дизайн C# следует тем принципам, которым он следует для C# имеет смысл использовать Task (вместо отдельных Async), что влечёт за собой ряд последствий. И я могу понять причины других решений это, вероятно, лучший способ интеграции асинхронного программирования в C#. Но в то же время я думаю, что F# справляется лучше отчасти из-за способности к компоновке, но, что более важно, из-за крутых дополнений, таких как агенты F#. Кроме того, у асинхронности в F# тоже есть свои проблемы (самая распространенная ошибка хвостовые рекурсивные функции должны использоваться return! вместо do!, чтобы избегать утечек), но это тема отдельной статьи для блога.

P.S. От переводчика. Статья написана в 2013 году, но она показалась мне достаточно интересной и актуальной, чтобы перевести её на русский. Это мой первый пост на Хабре, поэтому не пинайте сильно.
Подробнее..

Медленный код, зато красивый вообще не проблема, пока ты знаешь, как его ускорить

23.07.2020 18:11:16 | Автор: admin


Я много раз слышал, как программисты смеются над тиммейтами, которые написали медленный код. Резкие, самодовольные фразы в стиле "этот болван четыре раза пробежался по коллекции, хотя можно было один", и тому подобное. Когда слышишь такое, сразу думаешь ну тут все по делу, зачем делать лишние итерации? Почему нельзя изучить пару элементарных вещей, вроде принципов работы LINQ выражений в C#, и писать нормальный код? Ты смеешься над некомпетентными тупицами до тех пор, пока смеяться не начнут над тобой. И можете мне поверить никакие знания в программировании не спасут вас от ситуации, когда вы по незнанию зафигачили квадратичный алгоритм вместо линейного.


Я не знаю, смеялся ли кто-то над моим кодом, но я точно уверен не раз и не два я писал решение, которое было в сто раз медленнее, чем могло бы быть. Но в этих случаях я сознательно писал неоптимально. Например в C# есть цикл for, и есть метод коллекции Select (в других япах он чаще называется map). Цикл быстрее, но я считаю функциональный подход более эстетичным и использую его. Я знаю, что данных мало, тут не будет ботлнека, и пишу код, который по-моему красивей, читабельней и потому лучше. Таких решений при разработке очень много и я выбираю писать производительный код только когда точно знаю, что иначе просадки станут проблемой.


Есть очень много разрабов, которые делают по-другому, и искренне считают, что такие как я дерьмовые инженеры. У меня есть друг, опытный разраб высокого класса, который на днях выпнул человека с собеса, потому что тот предоставил медленное решение. Задача была реализовать специфичный LINQ метод. В C# эти методы следует делать ленивыми через итераторы и yeild а парень просто зафигачил новый список, и стал пихать в него значения, чем сломал ленивость. На больших срезах данных это может создать крупные и главное неожиданные просадки в производительности, достаточные, чтобы на доске завелся баг. По большому счету, тут дело даже не в снижении производительности, а в перемещении ботлнека в неожиданное место не самая приятная хрень.


Я бы никогда не поставил крест на разработчике из-за такого, потому что сам не заморачиваюсь с yield, пока данных не становится по-настоящему много. С новым листом сделать интуитивно проще, принципы работы LINQ я хорошо понимаю. У меня нет задачи писать какой-то совершенный, абсолютно отказоустойчивый код заранее, и даже тогда, когда мне оптимизация ничего не стоит, я не всегда её применю. И я такой не один. В майкрософтской доке по LINQ пример добавления своего метода предложен именно с новым списком потому что у создателя примера были дела поважнее, чем возиться с итераторами.


Мы позвали в подкаст Андрея Акиньшина человека, который действительно разбирается в производительности. Создатель BenchmarkDotNet, автор книги Pro .NET Benchmarking, топовый докладчик на темы производительности, поведения рантайма и бенчмаркинга в дотнете, и просто очень, очень, очень крутой инженер. Мой друг, который прогнал человека с собеса слепой котеночек в вопросах перформанса по сравнению с ним.


Знаете, что нам сказал Андрей? Что эстетика важнее производительности, пока у тебя нет ботлнеков.


Что если тебе больше нравится Select ты пишешь Select. Потому что когда ты действительно узнаешь, сколько вещей у тебя работают неоптимально, ты перестаешь верить, что сможешь поправить их все, и начинаешь верить, что исправлять их надо только тогда, когда тебе это действительно нужно.


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


И эти спичечники считают, что они крутые парни, а все остальные вшивые говноделы. Мне есть что им сказать. Если ты взял for вместо select, хотя оба варианта нас устраивают по скорости, то почему ты тогда остановился на этом? Какой ещё C#, братан, C# это медленно. Давай возьмем C++. А лучше ассемблер. Нет, стоп, ассемблер неоптимален давай сразу фигачить машинные коды. Эстетика, поддерживаемость, читабельность это же все херня собачья. Важно, чтобы все четыре элемента связного списка были выведены на экран наиболее быстрым способом.


Но машинные коды мы не хотим, потому что на них невозможно делать большие и сложные проекты. Мы хотим приятный, удобный и понятный C#. Его создатели разрулили сто тысяч трейдофов за нас. Мы получили дофига плюшек надежность, поддерживаемость, инструменты для переиспользования кода и заплатили за это производительностью. И наши приложения все ещё достаточно быстры, чтобы не думать об этом.




Хорошо, когда скорость и красивость не противоречат друг другу. Если по условиям задачи тебе больше подходит Queue, а не List взять очередь будет и эстетичнее, и производительнее. Но люди, которым совершенно не интересно быстродействие, никогда об этом не узнают. Вы легко отличите их шарповый код там везде будут листы.


Идея, что ты не должен оптимизировать свой код заранее понятна и справедлива. Но тут есть одна проблема. Иногда я пишу тормозной код не потому что все обдумал и принял решение иногда я понятия не имею, как его ускорить. Одно дело, когда сознательно выбираешь эстетику вместо производительности, другое когда ты ничего не выбираешь, потому что не умеешь делать производительно. Это очень, очень разные вещи.


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


За примерами далеко ходить не надо достаточно просто посмотреть на современных фронтендеров. Большая часть из них, когда увидят "производительность, C#, LINQ" захлопнут эту статью, и не дочитают до этого места. Так что теперь мы можем поговорить, что у них нет никакой инженерной культуры. Они делают самые тормозные вещи в мире вроде всяких электронов у них в языке из коробки всего два вида коллекций они про другие и знать не знают. Их инструменты для билдов и управления пакетами работают так медленно, что после ввода "yarn start" можно смело идти смотреть сериал. Итоговые файлы, которые получаются после компиляции весят раз в сто больше, чем должны были. Создается эффект снежного кома. Как теперь не переучивай их, какую культуру не прививай с большинством проблем фронтенд инфраструктуры особо ничего и не сделаешь если только не переписать её с нуля.


Мы не можем забить на производительность совсем это крайность. Но и сказать, что все должны полностью разбираться в ней разбираться, мы тоже не можем. Вопросов перфоманса бесконечность. Да, окей, я могу запомнить, какие структуры данных для чего подходят, усвоить, какие оптимизации умеет делать конкретный компилятор, и как не наступить в общеизвестные грабли в конкретном ЯПе. Но это самая верхушечка гигантского айсберга. И в мире нет человека, который бы продвинулся в этом направлении достаточно, чтобы с уверенностью писать код без потенциальных проблем с быстродействием. И вот поэтому я говорю про инженерную культуру.


Культура это когда стремление знать не становится культом, а невозможность познания не ведет к отказу от изучения. Это всегда компромиссы, но осознанные.


Нет ничего страшного, что ты выбрал покрасивее, а не побыстрее. И нет трагедии, когда ты просто не знал, что твое решение супер неоптимальное. Проблема начинается тогда, когда ты говоришь, что производительность это чушь, и пусть этим занимаются отдельные люди. Нет братан. Изучать, как можно ускорить код это не удел перформанс задротов, это часть нашей работы. Когда от твоего нежелания изучать сложные вещи твой код становится и медленным, и уродливым вот тогда у тебя большие проблемы.


Смотрите мой подкаст
Подробнее..

Вышел .NET 5. И что?

04.12.2020 10:17:32 | Автор: admin

Несколько недель назад вышел .NET 5. На сайте Microsoft можно найти подробный анонс со всеми изменениями, но главный вопрос для меня, как для разработчика и что с того? Что мне с выхода пятого дотнета, как я могу его использовать, есть ли смысл переходить на новую версию прямо сейчас? На эти вопросы я постараюсь ответить дальше.


image


Перфоманс


Изменения в перфомансе это самая заметная часть релиза. О них много писали на сайте Microsoft и рассказывали на .NET Conf. Давайте посмотрим внимательнее и определимся, где, насколько и за счет чего выросла производительность.


image


Измерения перфоманса с этого скрина основаны на бенчмарках от TechEmpower. Здесь Microsoft сравнивают результаты замеров производительности .NET 3.1 с ожидаемыми значениями для .NET 5. Стоит заметить, что это именно ожидаемые значения реальных результатов от TechEmpower пока не было. Однако уже эти результаты выглядят впечатляюще ускорение на 38 процентов для Plaintext, на 42 для Json и на 20 процентов в категории Fortunes (пояснения по разным типам бенчмарков вы можете найти в вики TechEmpower).


Помимо результатов бенчмарков от TechEmpower, Microsoft также рассказывает и о других улучшениях. Например, сериализация JSON стала работать на 20% быстрее, а сериализация больших коллекций и массивов ускорилась аж в три раза.


Также замеры производительности делал автор библиотеки Fusion Александр Якунин. В двух статьях на Medium он сначала измерил, насколько ускорилась работа демок его библиотеки (от 20 до 105 процентов), а затем проверил пятый .NET на нескольких синтетических бенчмарках и также получил некоторое ускорение.


За счет чего это происходит? В первую очередь, благодаря изменениям в GC, которые привели к уменьшению количества аллокаций и ускорению сборки мусора. Кстати, все эти изменения осуществляются одновременно с переписыванием GC на C#, а значит его код становится куда доступнее, понятнее и безопаснее. Подробности можно почитать в статье от Microsoft.


Другие изменения коснулись JIT компиляции, асинхронности и многих методов базовой библиотеки. Это касается методов, относящихся к работе со строками, регулярными выражениями и, что особенно важно, с коллекциями и сериализацией. Во все той же статье приводится много примеров таких изменений, с соответствующими пулл-реквестами и бенчмарками.


Изменения в JSON сериализации выглядят особенно впечатляюще. Только посмотрите на результаты бенчмарка по сериализации больших массивов!


private MemoryStream _stream = new MemoryStream();private DateTime[] _array = Enumerable.Range(0, 1000).Select(_ => DateTime.UtcNow).ToArray();[Benchmark]public Task LargeArray(){    _stream.Position = 0;    return JsonSerializer.SerializeAsync(_stream, _array);}

image


Коротко пятый дотнет сильно вырос в производительности и это важнейшая часть нового релиза. Большинство стандартных веб-приложений могут ожидать прирост производительности на 20% и выше, а отдельные приложения могут и вовсе ускориться в несколько раз.

Языковое


Вместе с .NET 5 вышла и девятая версия C#. Официальную страничку с изменениями вы можете найти на сайте Microsoft, но давайте попробуем разобраться что именно здесь стоит внимания?


Самое заметное изменение это record-типы, позволяющие избавиться от бойлерплейта при написании DTO. Записи это иммутабельные ссылочные типы с простым и коротким объявлением. В них по умолчанию определены методы Equals, HashCode, Copy, Clone, PrintMembers и ToString разумеется, все они используют значения полей типа для выполнения операций. То есть Equals корректно сравнивает две записи по значениям, а не по ссылке.


Также записи поддерживают синтаксис копирования с изменением значений полей через with, например:


var person = new Person { FirstName = "Mads", LastName = "Nielsen" };var otherPerson = person with { LastName = "Torgersen" };

Больше о применении записей вы можете почитать в блоге Владимира Хорикова и в публикации от Konrad Kokosa.


Другое важное изменение это обновленный pattern matching. Теперь паттерны могут настраиваться по типам и операторам сравнения, причем несколько разных сравнений вы можете объединять через логические операторы. Например:


DeliveryTruck t when t.GrossWeightClass switch {    < 3000 => 10.00m - 2.00m,    >= 3000 and <= 5000 => 10.00m,    > 5000 => 10.00m + 5.00m}

Остальные изменения не столь заметны они касаются верхнеуровневых программ (возможность писать короткие программы без базового класса и функции main), упрощенного синтаксиса для new (без указания типа), init-only сеттеров и прочего.


Последние изменения в новом C#, которые хотелось бы упомянуть это расширенная поддержка для source generators. Этот функционал позволяет генерировать код при компиляции проекта и зачастую очень удобен для авторов библиотек. В новом релизе работу с генераторами кода сделали чуть проще, расширив функционал partial классов (с них сняли некоторые из старых ограничений) и добавив инициализаторы модулей (методы, которые вызываются до первого доступа к любому полю/методу модуля).
Хороший пример работы с генераторами кода вы можете найти в этой статье на Medium.


Отдельно стоит рассказать про F# 5. В новой версии языка добавилась масса классных возможностей для скриптов (например, директива для подключения nuget пакетов), slicing для более удобной работы с данными, а также улучшилась работа с quotation expressions и computation expressions. А еще сильно улучшился интероп с C# и перфоманс. Словом, новый релиз выглядит интересно и делает F# потенциально полезнее в продакшене. Полное описание всех изменений можно найти на сайте Microsoft.


Коротко язык оброс разнообразным сахаром. Самое заметное изменение в C# 9 это записи, но едва ли оно само по себе стоит обновления. С другой стороны, если вы будете обновлять версию дотнета по каким-то другим причинам, то у вас появится новый функционал, делающий язык чуть более мощным. Мелочь, а приятно.

Прочее


Помимо глобальных улучшений платформы Microsoft также неплохо поработали над отдельными библиотеками.


Например, в новой версии дотнета сильно улучшилась поддержка десктопа. WinForms и WPF наконец-то получили все преимущества .NET Core классный перфоманс, упаковка всего приложения в один .exe, работа без установленного на машине дотнета и и нет, не кроссплатформенность. Все это работает только под Windows.


Помимо прочего, для десктопной разработки под Windows также добавили нормальный визуальный дизайнер, тулинг, новые контролы и улучшенную поддержку для старых. Словом если вы разрабатываете приложения с использованием WinForms или WPF, ваша жизнь станет лучше.


С кроссплатформенностью все осталось по-прежнему. Релиз MAUI перенесли на .NET 6, а других решений для кроссплатформенного десктопа Microsoft пока не предлагает. Так что продолжаем использользовать Авалонию (или можно обратить свое внимание на Uno Platform перспективно выглядящий фреймворк для разработки на все и сразу).


Еще больше улучшений появилось для фулстек веб-разработки с использованием Blazor. Главное из них это улучшения производительности Microsoft обещают, что WebAssembly версия Blazor ускорится аж в три раза. Одной из основных причин такого роста производительности стал пререндер на стороне сервера. А еще добавили ленивую подгрузку зависимостей, изоляцию CSS/JS в рамках файла, новые контролы и многое другое. В общем, если вы собирались попробовать Blazor, но все никак не доходили руки сейчас он выглядит куда более production-ready технологией.


image


Из оставшихся изменений заметнее всего смотрятся те, которые призваны помочь микросервисам и работе с облачными сервисами в целом. Здесь и уменьшение размеров Docker-контейнеров, и улучшенная поддержка gRPC, и поддержка OpenAPI по умолчанию, и многое другое. Кстати, Azure полностью поддерживает .NET 5 со дня релиза.


А еще Microsoft выкатили Project Tye, который как раз является инструментом для удобного управления микросервисами. На текущий момент трудно сказать, стоит ли использовать Tye в продакшене, но обратить внимание и поиграться с ним в свободное время определенно нужно.


Коротко Microsoft улучшили поддержку отдельных инструментов и добавили приятных фич для облака. Если вы используете WinForms, WPF или Blazor обновиться определенно стоит.

Миграция


Давайте разберемся, насколько сложно будет мигрировать ваше приложение с .NET Core 3 на .NET 5?


Гайд по миграции от Microsoft говорит нам, что основные сложности в миграции коснутся приложений на Blazor, там произошло много ломающих изменений. Для большинства же остальных приложений понадобится просто обновить версию дотнета в application.json, файл проекта и версии зависимостей. А еще адрес докер-контейнера с .NET 5 немного отличается от соответствующего адреса для .NET 3.1:


docker pull mcr.microsoft.com/dotnet/core/aspnet:3.1docker pull mcr.microsoft.com/dotnet/aspnet:5.0

Другие отзывы о миграции, которые я смог найти например, вот этот гайд от Alexandre Malavasi и твит о миграции Fusion от Александра Якунина тоже говорят, что миграция проходит без каких-то специфических трудностей. Я также не столкнулся с особыми проблемами, пока мигрировал свои проекты и демки на новый дотнет. В любом случае, перед миграцией на новую версию советую внимательно изучить список ломающих изменений. А еще стоит посмотреть доклад про миграцию с .NET Conf.


Итого


Подведем итоги. Стоит ли мигрировать на новый дотнет и ради чего?


Кажется, самое важное, ради чего стоит заморачиваться это перфоманс. Ускорили практически все и достаточно заметно. Как на низком уровне через улучшения в GC и JIT так и на уровне отдельных частей фреймворка. Так что, если вы хотите выиграть в производительности, стоит как минимум попробовать обновить версию фреймворка и замерить, насколько ускорились основные сценарии.


Другие важные причины для миграции это улучшения для Blazor и WPF/WinForms. Если вы используете любой из этих фреймворков, стоит попробовать перейти на .NET 5, все же изменения достаточно заметные и полезные. Однако стоит учитывать, что для Blazor миграция выйдет достаточно непростой.


C# 9 определенно не является серьезным мотивом для переезда на новый дотнет, но принесет с собой приятных обновлений синтаксиса за компанию. В то же время и рекорды, и паттерн матчинг добавляют много возможностей сделать код более запутанным советую обсудить это в команде и решить, как лучше использовать (или не использовать) их в проекте.


И на этом все! Удачной миграции на новый дотнет. Оставайтесь на связи, мы вернемся с более подробными материалами про современные возможности дотнета.

Подробнее..

Jupyter для .NET. Как в питоне

18.11.2020 18:12:16 | Автор: admin
Несколько месяцев назад Microsoft рассказали о Jupyter в .NET. Но активности по этому топику очень мало, а ведь тема очень интересная. Но что такое прикольное придумать? Я решил сделать удобный вывод класса Entity из библиотеки символьной алгебры:



Выглядит круче, чем в питоне. Делается просто, доставляет массу удовольствия. Приглашаю под кат!



О Jupyter


Это что-то вроде IDE для создания интерактивных блокнотиков. Вместо того, что бы в привычных средах запускать весь код сразу, здесь вы запускаете его по кускам, сохраняя состояния переменных. Очень удобно для исследований и простых скриптов чего-нибудь подсчитать.

О dotnet/interactive


Этот проект как раз позволяет встроить дотнет в юпитер. То есть вы буквально можете написать

И получать результаты работы кода прямо вот сразу.

Причем некоторые фишки работают из коробки


Об AngouriMath


Это относительно небольшая библиотека символьной алгебры для .NET для работы с математическими выражениями. Разумеется, с математическими выражениями можно работать в одну строку, но это не всегда удобно/красиво.

Все математические выражения так или иначе наследуются от класса Entity, который имеет метод Latexise, возвращающий строку. Поэтому остается всего лишь его отрендерить.

Встраиваем латех


У нас есть возможность зарегистрировать собственный вывод для наших типов, что я и делаю:

let magic() =    let register (value : ILatexiseable) = $@"            <script src='https://polyfill.io/v3/polyfill.min.js?features=es6'></script>            <script id='MathJax-script' async src='https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js'></script>            \[{value.Latexise()}\]            "    Formatter.Register<ILatexiseable>(register, "text/html")

(хабр почему-то не поддерживает F#)

То есть мы просто регистрируем, что тип ILatexiseable должен выводиться в html-формате, а сам код будет генерироваться нашей функцией. Для рендеринга я взял MathJax, хотя наверное имеет смысл кешировать js-скрипт, но это для будущих обновлений.

Ну и собственно все, теперь все выражения, унаследованные от этого интерфейса, будут красиво рендериться. Вот как это выглядит в C#:



Что именно тут происходит?
1. В первом блоке мы вызываем extension-метод ToEntity(), который парсит выражение
2. Во втором блоке мы создаем систему уравнений и сразу ее выводим
3. В третьем создается матрица и сразу выводится


Так как юпитер предзназначен для небольших участков кода, которые не обязаны следовать каким-то проектным требованиям, F# подходит лучше для этого. Поэтому, как это понятно из скриншота в начале статьи, он тоже поддерживается. Для примера, так выглядит решение школьного уравнения:



Дальнейшие планы


Я очень большой любитель .NET-а, но я также очень люблю Jupyter. Поэтому Interactive меня очень порадовал, и я поспешил сделать поддержку Interactive для AngouriMath для вывода выражений в LaTeX. Но дальше интереснее. Я думаю сделать что-то типа Entity.Plot(), который выводил бы сразу график функции. Для простых use-кейсов очень нужна штука, мне кажется.

Если хочется попробовать не устанавливая ничего из необходимого, вы можете потыкаться тут: (аларм: почему-то долго грузится, придется подождать)

Спасибо за внимание! Такая вот короткая заметка.

Ссылки


1. Jupyter удобная браузерная среда для интерактивного программирования
2. .NET Interactive та самая гениальная вещь, благодаря которой можно использовать дотнет в юпитере
3. AngouriMath математическая библиотека, для которой я написал оболочку для латеха
4. MyBinder демка для ленивых
Подробнее..
Категории: C , Математика , Net , F , Jupyter notebook , Jupyter , Latex , Angourimath

Перевод Букварь по F для любопытствующих C-разработчиков

07.02.2021 14:05:43 | Автор: admin

Предисловие


Мой переход на F# в качестве излюбленного языка был слегка усеян препятствиями. Примерно через десять лет почти постоянного использования C# у меня пробудилось любопытство, когда я услышал об этом другом #-языке. Моя первая реакция была той, которую с тех пор видел у других C#-разработчиков отрицание, C# является хорошим языком, и мне с ним комфортно, так зачем тратить силы на изучение другого? Но любопытство осталось и, по крайней мере, несколько раз выделил вечер, чтобы прочитать базовый вводный пост и попытаться написать каких-нибудь ката на F#. Это не прижилось, потому что я просто чувствовал себя потерянным и не мог воплотить свой опыт использования C# в ощущение даже отдаленного комфорта с F#. Достаточно легко опустить фигурные скобки, немного замяться, чтобы не забыть let вместо var но как сделать то, что я хотел?


Тогда я этого не осознавал, но, на мой взгляд, наблюдал потенциальный недостаток в том, как F#-разработчики говорят, описывают и представляют свой язык внешнему миру. Существует обширная база материалов обо всех возможностях и функциональности F#: Algebraic Data Types, Exhaustive Matching, Type Inference и т.д. Есть много статей, посвященных тому, как решать широкий спектр задач с помощью F#. Но, как мне кажется, не хватает чего-то вроде следующего: некоторых указаний о том, как взять то, что вам уже удобно в C#, и перевести их на F#. Так что мне интересно, можем ли мы как-то закрыть этот недостаток.


При этом от читателя требуется немного поверхностное знакомство с тремя основными моментами синтаксиса F#:


  • let используется как var в C# для объявления переменной;
  • |> это оператор пайпа (piping) в F#, который берет результат левой части и передает его в качестве аргумента для правой части;
  • F# использует строчные буквы и апостроф для аннотаций обобщенного типа, поэтому SomeType<T> представлен как SomeType<'a>.

Остальное должно быть понятно из практики и контекста по мере продвижения. Это не должно быть исчерпывающим, замысловатым руководством, но обладать достаточной информацией, чтобы охватить большинство начальных вопросов и поставить людей на правильный путь. Букварь, если хотите.





Мне необходимо


Работать с коллекциями


В F# базовые типы коллекций (в основном) как правило очень похожи на C#, но часто имеют (иногда незначительные) различия в поведении для обеспечения иммутабельности. В большинстве случаев функции, которые работают с этими коллекциями, будут возвращать ссылки и не будут изменять содержимое исходной ссылки.


Подобрать тип коллекции


Что-то похожее на Array<T>

Тебе повезло! Массивы в F# такие же как в C#. Однако следует отметить несколько моментов:


  1. Массивы в F# обычно используют нотацию [|element|], потому что [] это нотация для списков в F#.


  2. Для разделения элементов коллекции в F# используется точка с запятой, а не запятая: [|elementA;elementB|].


  3. Для доступа по индексу в F# требуется префиксная точка перед фигурными скобками:


    let myArray = [|1;2;3|]myArray.[1] // 2
    

  4. F# также предлагает многомерные массивы до 4-х измерений через типы Array2<'a>, Array3<'a> и Array4<'a>.



Что-то похожее на List<T>

По умолчанию в F# тип списка немного отличается от типа List<T> в C#.


Вот что вам нужно знать:


  1. Списки в F# обычно используют нотацию [element] в отличие от массивов.


  2. Списки, как и массивы, разделяют элементы точками с запятой вместо запятых: [elementA;elementB]


  3. Списки в F# реализованы как односвязные списки это означает, что добавление отдельных элементов выполняется в начале списка с помощью оператора :::


    let myList = [1;2;3]4 :: myList // [4;1;2;3]
    

  4. Если нам необходимо добавить в конец, мы можем использовать оператор @ для объединения двух списков:


    let listA = [1;2]let listB = [3;4]listA @ listB // [1;2;3;4]
    


Что-то похожее на Dictionary<TKey,TValue>

По мотивам списка выглядит похоже, но не нет F# предоставляет стандартный Map<'key,'value> тип, который не является родным для C# Dictionary<TKey,TValue>, но реализует обычную группу интерфейсов .NET, таких как IDictionary<TKey,TValue> и IEnumerable<T>


Вот что вам нужно знать:


  1. Словари могут быть созданы из любой коллекции двух элементных кортежей, где первый элемент является ключом, а второй значением:


    [(1,2);(3,4)] |> Map.ofList // [1] = 2, [3] = 4
    

  2. Если создаем из последовательности, где есть дубликаты, то последний элемент для данного ключа является значением:


    [(1,2);(1,3)] |> Map.ofList |> Map.find 1 = 3 // true
    

  3. Верен и обратный процесс: словари можно легко превратить в коллекции кортежей из двух элементов:


    [(1,2);(3,4)] |> Map.ofList |> Map.toList // [(1,2);(3,4)]
    

  4. Встроенный тип Map в F# не очень хорошо подходит для использования в C#, в случаях интеропа мы можем создать более удобный для C# словарь IDictionary, используя функцию dict с любой коллекцией кортежей из двух элементов. Но учтите, что это по-прежнему неизменяемая структура, и при попытках добавить в нее элементы будет генерироваться исключение.


    [(1,2);(3,4)] |> dict
    


Подобрать функцию


Одно важное различие между F# и C#, когда дело доходит до работы с коллекциями, заключается в том, что в C# вы, как правило, оперируете над экземпляром коллекции, используя метод этого типа через точку; в то время как F# предпочитает предоставлять семейства функций в модулях, которые принимают экземпляры в качестве аргумента. Итак, C#-вариант myDictionary.Add(someKey,someValue) в F# будет Map.add someKey someValue myMap.


Просто хочу свой LINQ


F# предлагает функции, аналогичные тем, с которыми программисты на C# знакомы по LINQ, но названия часто отличаются, поскольку F# использует систему условных обозначений, которая больше соответствует терминологии, используемой в остальной части мира функционального программирования. Будьте уверены, они в основном ведут себя так, как вы и ожидаете. Дабы не утомлять LINQ огромен, я сопоставлю, по моему опыту, наиболее распространенные методы LINQ и их аналоги на F#:


  • .Aggregate() именуется как .fold или .reduce, в зависимости от того, предоставляете ли вы начальное состояние или просто используете первый элемент, соответственно;
  • .Select() именуется как .map;
  • .SelectMany() именуется как .collect;
  • .Where() именуется как .where или .filter (одно и то же, два имени, длинная история)
  • .All() именуется как .forall;
  • .Any() именуется как .exists, если мы подаем предикат, или .isEmpty, если мы просто хотим знать, есть ли в коллекции какие-либо элементы;
  • .Distinct() по-прежнему как .distinct или .distinctBy, если мы подаем функцию проекция;
  • .GroupBy() по-прежнему как .groupBy;
  • .Min() и .Max() по-прежнему остаются как .min и .max с альтернативами .minBy и .maxBy для использования проекции
  • .OrderBy() именуется как .sortBy, и аналогично .OrderByDescending() именуется как .sortbyDescending;
  • .Reverse() именуется как .rev;
  • .First() именуется как .head, если нам нужен первый элемент, или .find, если нам нужен первый элемент, который соответствует предикату. Точно так же вместо .FirstOrDefault() мы используем .tryHead и .tryFind, которые вернут Option, являющимся либо Some matchingValue, либо None, когда он не найден, вместо того, чтобы выбрасывать исключение;
  • .Single() именуется как .exactlyOne, и аналогично .SingleOrDefault() именуется как .tryExactlyOne.

Не уверен, какая функция нужна. У меня есть


Коллекция, а хочу

Отдельное значение или элемент

  • .min, .minBy, .max и .maxBy найдут элемент коллекции относительно других;
  • .sum, .sumBy, .average, .averageBy;
  • .find, .tryFind, .pick и .tryPick позволят найти один конкретный элемент коллекции;
  • .head, .tryHead, .last и .tryLast найдут элементы из начала или конца коллекции;
  • .fold и .reduce позволят применить логику и использовать каждый элемент коллекции для формирования другого значения;
  • .foldBack и .reduceBack делают то же самое, но с конца коллекции.

Равное количество элементов

  • .map позволит преобразовать каждый элемент коллекции;
  • .indexed свернет каждый элемент вашей коллекции в кортеж, первым элементом которого является индексом в коллекции: например, [1] станет [(0,1)];
  • .mapi делает это неявно, учитывая индекс в качестве дополнительного первого аргумента функции маппинга;
  • .sort, .sortDescending, .sortBy и .sortByDescending позволяют изменить порядок вашей коллекции.

Возможно меньшее количество элементов

  • .filter вернет коллекцию, содержащую только элементы, соответствующие указанному предикату;
  • .choose похож на .filter, но заодно позволяет маппить элементы;
  • .skip вернет оставшиеся элементы после игнорирования первых n;
  • .take и .truncate возвращают первые n-элементов, выбрасывая или нет исключение, соответственно;
  • .distinct и .independentBy позволят удалить дубликаты из коллекции.

Возможно большее количество элементов

  • .collect применит функцию создания коллекции к каждому элементу вашей коллекции и объединит все результаты воедино.

Чтобы изменить форму коллекции

  • .windowed вернет новую коллекцию всех групп размером n из исходной коллекции: например, [1; 2; 3] станет [[1; 2]; [2; 3]], когда n = 2;
  • .groupBy вернет новую коллекцию кортежей, где первый элемент является ассоциативным ключом, а второй набором начальных элементов, которые соответствуют ассоциации: например, [1; 2; 3], преобразованной (fun i -> i % 2), приведет к [(0, [2]); (1, [1; 3])];
  • .chunkBySize вернет новую коллекцию, содержащую до n коллекций оригинала: например, [1; 2; 3] станет [[1; 2]; [3]], когда n = 2;
  • .splitInto вернет новую коллекцию, содержащую n коллекций одинакового размера из исходного: например, [1; 2; 3] станет [[1]; [2]; [3]], когда n = 3.

Чтобы пройти по коллекции без ее изменения

  • .iter и .iteri берут и применяют функцию к каждому элементу вашей коллекции, но не возвращают никакого значения.

Отдельное значение и хочу

Чтобы было частью коллекции

  • .singleton можно использовать для создания коллекции из одного элемента из значения;
  • .init примет размер и функцию инициализатора и создаст новую коллекцию этого размера.

Несколько коллекций и хотите

Скомбинировать их

  • .append принимает две коллекции и создает новую единую коллекцию, содержащую все элементы обеих;
  • .concat делает то же самое, но для коллекции коллекций;
  • .map2 и .fold2 действуют как выше указанные .map и .fold, но будут предоставлять элементы из одного индекса в двух исходных коллекциях для функции маппинга / свертки;
  • .allPairs принимает две коллекции и образует все перестановки по 2 элемента между ними;
  • .zip и .zip3 берут 2 (или 3) коллекции и создают одну коллекцию, состоящую из кортежей элементов из одного индекса в источниках.

Работать асинхронно


Модель асинхронности в F# похожа на модель в C#, но имеет несколько важных отличий, которые иногда застают врасплох C#-разработчиков:


  1. F# имеет отдельный тип Async<'t>, похожий на Task<T> в C#.


  2. Из-за того, что система типов F# требует возврата, она использует Async<unit> вместо Task в случаях, когда мы не возвращаем фактического значения.


  3. F# может генерировать и использовать Task<T> с помощью функций Async.StartAsTask и Async.AwaitTask из базовой библиотеки.



У F# есть еще одно очень заметное отличие от C# в отношении асинхронного кода: C# "включает" ключевое слово await внутри метода, применяя ключевое слово async к сигнатуре этого метода; F# использует языковую функцию, называемую computation expression, в результате чего асинхронность становится частью тела функции. Это также имеет некоторые последствия на то, как вы пишете код внутри этого тела функции:


let timesTwo i = i * 2 // У нас есть определение нашей базовой функции// И теперь мы можем сделать это асинхроннымlet timesTwoAsync i = async { // Обратите внимание, что при работе с computation expression мы начинаем с нашего ключевого слова, а затем с самой функции внутри фигурных скобок   return i * 2 // Мы также используем ключевое слово `return` для завершения выражения}let timesFour i = async {    let! doubleOnce = timesTwoAsync i // Обратите внимание  на `!` в нашем `let!`  это похоже на `await` в C#  правосторонняя функция должна возвращать `Async<'a>`    // После того, как мы связали результат асинхронной функции с помощью `let!`  мы можем использовать его потом как обычно    let doubleTwice = timesTwo doubleOnce // В случае неасинхронных функций мы можем написать наш код как обычно    return doubleTwice}

  1. Имейте в виду, что let! в Async-блоках работают только при вызове Async-образующих функций аналогично тому, как в C# await можно использовать только для методов, возвращающих Task.


  2. Другой путь, однако, заключается в том, что поскольку F# обрабатывает асинхронность исключительно в теле функций, нет никаких требований о том, какие функции вы можете связывать с let! все, что возвращает Async<'a>, допустимо. Это противоположно требованиям C# о том, что вы можете применять await только к методам, помеченным как async.



Сообщать об ошибке или контролировать выполнение программы


Во-первых, определение: когда мы говорим об ошибках и выполнении программы, я не имею в виду исключения в F# они есть и вполне схожим образом работают как в C#. Я имею в виду предсказуемые и потенциально исправимые ошибки; потому что эта та область, в которой F# с первого взгляда может показаться похож на C#, но очень быстро становится очевидно, насколько они различаются. В частности, это проявляется в использовании значения null как распространенного сигнала об ошибки в C#. Это не редкий паттерн в C#, который выглядит примерно так:


public Foo DoSomething(Bar bar){    if (bar.IsInvalid)    {        return null;    }    return new Foo(bar.Value);}

И затем, вызывающий DoSomething может проверить возвращаемое значение на null и либо обработать, либо передать его дальше. По моему опыту, одна из областей, где это часто возникает это функция LINQ FirstOrDefault(), которая используется, чтобы избежать исключения в случае пустого IEnumerable<T>, но часто заканчивается просто продвижением дальше null.


Изначально кажется, что F# пытается осуществить это с помощью своего типа Option<'a> и часто возникает вопрос: не является ли None просто ярлыком для null, за исключением того, что теперь труднее получить значение обернутое в Some? Потому что для этого потребуется pattern matching или проверка .HasValue для опции и действительно ли это лучше? Это не так, и именно поэтому F# посредством функционального программирования предлагает более чистое решение: разрабатывать основную часть кодовой базы, не беспокоясь о проверке на существующие ошибки, а вместо этого беспокоясь только об оповещении потенциально новых, специфичных для данной функции. Мы можем сделать это, написав большинство наших функций так, как будто входные данные уже были проверены для нас, и затем, с помощью функций map или bind, связать наши безответственные функции вместе. Давайте посмотрим на них в контексте Option:


  • map требуется два аргумента: функция 'a -> 'b и Option<'a>, из которых она будет генерировать Option<'b>;
  • bind также требует два аргумента: функция 'a -> Option<'b> и Option<'a>, из которых она будет генерировать Option<'a>.

Давайте посмотрим, что они могут для нас сделать:


// string -> Option<string>let getConfigVariable varName =    Private.configFile    |> Map.tryFind varName// string -> Option<string[]>let readFile filename =    if File.Exists(filename)        then Some File.ReadLines(filename)        else None// string[] -> intlet countLines textRows = Seq.length filegetConfigVariable "storageFile"                 // 1|> Option.bind readFile                         // 2|> Option.map countLines                        // 3

Так что тут происходит?


  1. Мы пытаемся взять переменную из нашей конфигурации. Может быть, она существует, а может и нет, но это имеет значение только для этой единственной функции.
  2. Затем мы перенаправляем в Option.bind который неявно обрабатывает логику безопасности для нас: если предыдущий шаг имеет значение Some используйте его в качестве аргумента этой функции, в противном случае оставьте его как None и двигайтесь дальше.
  3. Option.map делает то же самое если есть значение Some, используйте его с этой функцией, в противном случае просто двигайтесь дальше.

Прозорливый наблюдатель заметит, что на шаге 3 нет непосредственной разницы между bind и map они оба автоматически обрабатывают одно и то же, верно? Но обратите внимание на разные сигнатуры между readFile и countLines bind имеет дополнительный шаг, который производит flatten (прим. перев.: разворачивает вложенную структуру, Option.flatten) над параметром Option, который выводит его функция. Рассмотрим альтернативу: если бы мы использовали map, то в конце строки 2 у нас было бы Option<Option<string[]>> и так в строке 3 нам потребуется Option.map (Option. map countLines)!


Но возникает вопрос, как мне на самом деле получить значение, если оно есть выводом этого Option? И это справедливый вопрос. И ответ избегать этого как можно дольше. Поскольку, чем позже вы откладываете попытку развернуть Option, тем меньше кода вам нужно написать, который хоть как-то предполагает, что ошибка возможна. И в тот момент, когда вам, наконец, определенно необходимо получить значение, у вас есть два варианта:


  • Option.defaultValue принимает 'a и Option<'a> если Option имеет значение, он возвращает его, в противном случае он возвращает значение 'a, которое вы ему дали.


  • Option.defaultWith то же самое, но вместо значения для генерации значения требуется функция unit -> 'a.



Так уж совпало, что та же самая логика применима к встроенному в F# типу Result<'a,'b>, который также предлагает bind и mapmapError, если вам это нужно) но вместо None у вас есть вариант Error, который вы можете использовать для хранения информации о том, что пошло не так будь то string или пользовательский тип ошибки по вашему выбору.


Использовать C#-библиотеки в F


Одно из восхитительных преимуществ F# и, вероятно, почему C#-разработчик сначала смотрит на него, а не на что-то вроде Haskell, это то, что он является частью большой экосистемы .NET и поддерживает взаимодействие со всеми C#-библиотеками, с которыми разработчик уже знаком. Код на C# может (в основном) использоваться в F#, но иногда возникают некоторые затруднения, но обычно с легкими обходными путями:


  • При вызове C#-методов компилятор F# рассматривает метод как кортеж с одним аргументом. Из-за этого частичное применение строго невозможно, и пайпинг может быть затруднен из-за перегрузки:


    "1" |> Int32.Parse                          // Подобно Int32.Parse("1")("1", NumberStyles.Integer) |> Int32.Parse  // Подобно Int32.Parse("1", NumberStyles.Integer)NumberStyles.Integer |> Int32.Parse "1"     // Не компилируется, потому что ожидает кортежный аргумент, а не два отдельных аргумента.
    

  • C#-Библиотеки особенно те, которые включают сериализацию или рефлексию, часто не приспособлены для понимания встроенных типов F#. Наиболее распространенным случаем здесь являются библиотеки JSON, которые могут затрудняются над сериализацией и/или десериализацией Unions и Records в таких случаях настоятельно рекомендуется проверить на существование библиотеки расширений, которая предоставляет специфичную функциональность F#. Например, Newtonsoft.Json имеет пакет Newtonsoft.Json.FSharp, System.Text.Json FSharp.SystemTextJson. С другой стороны, в этих случаях может быть также хорошо проверить нативные библиотеки на F# подобно Thoth или Chiron.


  • Благодаря возможности C# создавать null для любого ссылочного типа, и отсутствию (на момент написания) (прим. перев.: fsharp/fslang-suggestions#577) встроенного интеропа для обозначения nullable reference type в C#, полезно попытаться изолировать код C# на внешнем уровне вашей логики и использовать утилиты, такие как Option.ofNullable (для Nullable<T>) или Option.ofObj (для ссылочных типов), чтобы быстро обеспечить безопасность типов для вашего собственного кода.


  • Методы в C#, которые ожидают типы делегатов, такие как Action<T> или Func<T>, могут получить лямбда-выражение F# соответствующей сигнатуры, и компилятор будет обрабатывать преобразование. Помните: unit заменяет void в F# и его () значение поэтому Action<T> будет ожидать 'T -> unit, например (fun _ -> printfn "I'm a lambda!"); и аналогично, Fun <T> ожидает unit -> 'T, например (fun () -> 123).


  • В тех случаях, когда C#-библиотека ожидает, что объекты будут декорированы атрибутами, то для этого используется хитрость в виде <>, которую F# использует внутри квадратных скобок так что [Serializable] C# превратится в [<Serializable>] F#. Аргументы работают одинаково: [<DllImport('user32.dll', CharSet = CharSet.Auto)>]. И, как и в случае с коллекциями выше, несколько атрибутов разделяются точкой с запятой, а не запятой: например, [<AttributeOne; AttributeTwo>].


Подробнее..
Категории: C , Net , F , Net c# f# primer

Работаем с notebook в VS Code с помощью расширения dotnet interactive

20.02.2021 14:06:35 | Автор: admin
Скриншот notebook'a из VS CodeСкриншот notebook'a из VS Code

Сегодня я хочу рассказать вам о таком замечательном инструменте как "dotnet interactive". Я покажу на своём примере как и для чего я начал его использовать, и вкратце опишу с чего начать.

Проблема

Моя работа связана с разработкой программы, предназначенной для оценки эффективности средств физической охраны. Данный продукт в своём ядре содержит имитационную модель, результатом которой является файл протокола, в котором описаны события моделируемого эксперимента. Это могут быть такие события как нарушитель попал в зону камеры или группа реагирования ведет преследование нарушителя и прочие подобные события, которые могут происходить при моделировании.

По итогам протокола считаются различные статистические метрики, которые потом уходят в отчёт. Сейчас формулы, как считать нужную нам статистику, разбросаны по различным этапам ТЗ, поэтому, когда я вчера узнал о "dotnet-interactive" мне сразу пришла мысль о создании notebook'a в формате "Описание метрики"-"Формула"-"Код"-"График", в котором можно будет загрузить любой файл протокола и в интерактивном формате проходить и считать интересующие нас метрики.

Приступаем к созданию notebook'a

Прежде всего, у вас должен быть установлен .net5 sdk и последняя версия VS Code. Далее, нужно лишь установить расширение ".NET Interactive Notebooks". Данное расширение сейчас имеет статус "Preview", однако уже сейчас там можно делать много интересных вещей.

Когда мы установили расширение, можем создать рабочую директорию, в которой будут лежать нужные библиотеки, скрипты и файлы. Открываем её в VS Code и окне команд вбиваем ".NET Interactive: Create new blank notebook" и начинаем наполнять наш notebook.

В первом блоке кода я определил загрузку файла протокола:

#load "Load.fsx"open Loadlet Experiment = loadSep "2021.02.03_15.55.58_gen.sep"

Здесь я на F# подключил скрипт, который инкапсулирует в себе логику открытия файла и xml-сериализацию:

#r "nuget: System.Text.Encoding.CodePages"#r "AKIM.Protocol.dll"open System.IOopen AKIM.Protocolopen System.Xml.Serializationopen System.Textlet loadSep path=        let deserializeXml (xml : string) =        let toBytes (x : string) = Encoding.UTF8.GetBytes x        let xmlSerializer = XmlSerializer(typeof<Experiment>)        use stream = new MemoryStream(toBytes xml)        xmlSerializer.Deserialize stream :?> Experiment    Encoding.RegisterProvider(CodePagesEncodingProvider.Instance)        deserializeXml (File.ReadAllText(path, Encoding.GetEncoding(1251)))

В этом скрипте я подключаю nuget пакет для кодировки и свою библиотеку с dto-классами, написанную на C#.

Во втором блоке notebook'a уже в c#-intaractive я подключаю нужные для работы с экспериментом пространства имён и шарю объект в с# из f#, определенного в первом блоке

#r "AKIM.Protocol.dll"using AKIM.Protocol;using AKIM.Protocol.Events;using AKIM.Protocol.Events.OperatorSvn;using AKIM.Protocol.Events.OperSb;using AKIM.Protocol.Events.RespUnits;using AKIM.Protocol.Events.Intruders;using AKIM.Protocol.Events.Sens;using AKIM.Protocol.Events.System;#!share --from fsharp Experiment

Собственно дальше мы можем в произвольном порядке описывать и считать нужные нам метрики, используя для этого объект эксперимента. Например, следующий блок выводит общее количество проникновений на объект и количество отказов нарушителя от проникновения

var allTests = Experiment.Tests.Count;var penetrations = Experiment.Tests.Where(t => t.Events.Last() is PenetrationEvent).Count();var nonPenetrations = Experiment.Tests.Where(t => t.Events.Last() is NonPenetEvent).Count();var eve = Experiment.Tests.First().Events.FirstOrDefault(t => t is VisContactEvent);Console.WriteLine(eve?.GetDescription());Console.WriteLine($"Количество проникновений {penetrations} из {allTests}")

Нажав на запуск выполнения кода мы получаем следующий вывод:

А дальше я могу использовать полученные значения для построения красивой диаграммы или графика, например:

#r "nuget: XPlot.Plotly"#!share --from csharp penetrations #!share --from csharp nonPenetrations#!share --from csharp allTestsopen XPlot.PlotlyChart.Pie(seq {("Кол-во проникновений",penetrations);               ("Нейтролизовали",allTests- penetrations-nonPenetrations);               ("Отказ от проникновения",nonPenetrations)}) |> Chart.Show

При выполнении график открывается у меня в браузере, хотя я видел, как в некоторых туториалах он открывается снизу блока с кодом.

Итоговая диаграммаИтоговая диаграмма

Что дальше

В дальнейшем, есть идея описать язык взаимодействия с событиями на F# в виде отдельной библиотеки или скрипта, как это было описано, например, тут. Так как, на мой взгляд, подобный notebook должен заполнять аналитик проекта, а не программист.

Что не работает?

Мне не удалось загрузить файл скрипта на С# с расширениями "*.csx" в с#-interactive. Возможно еще не завезли, возможно не правильно готовлю. Плюс не удалось решить, почему графики открываются в браузере, а не снизу блока с кодом. Также в markdown блоке не хотят отображаться формулы в формате $$...$$.

Выводы

Я считаю, что этот инструмент должен попробовать каждый .net разработчик. Вариантов использования масса: обработка результатов, прототипирование каких-то идей, изучение F# или C#, скриптинг для работы с операционной системой, например, чтобы manage'ить какие-то файлы. Лично я, когда случайно узнал вчера об этом инструменте, был в диком восторге, что и побудило меня сделать этот пост, так как мало, кто об этой штуке слышал (но это не точно).

Хочу поблагодарить своего подписчика на ютубе, Arkadiy Kuznetsov, который подсказал мне о существовании такого инструмента.

Жду отзывов об использовании этой штуки в комментариях + возможно, кто-то даст подсказки на решение проблем, которые у меня возникли с графиками и загрузкой c# скриптов.

Спасибо за внимание.

Полезные ссылки

Официальная репа, в которой есть также документация
.NET Interactive + ML.NET
Новые фичи f#(в начале видео использует dotnet-intaractive)

Подробнее..

Созвоны не решают никаких проблем. Они нужны только людям, которые не умеют писать код

07.07.2020 18:11:11 | Автор: admin


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


Я подумал ну окей, так, наверное, бывает не всегда. С тех пор прошло лет 5, я не раз менял работу, но везде и всегда созвоны были пустой тратой времени.


Вы, парни, работаете в IT, и отлично знаете, насколько гигантская часть нашей работы созваниваться. Я тоже знал. Мой средний рабочий день содержал пару часов этой болтовни, и это становилось невыносимым. Я начал нервничать наверное, я не смогу нормально работать разрабом, если дурацкие созвоны так сильно меня нервируют.


Чем больше я работал, тем больше их ненавидел. Ненавидел сами созвоны и ещё больше ненавидел людей, которые их инициируют. Каждый раз на собесе я слышал это вот мерзкое "мы работаем по аджайлу, у нас принято проводить дейли синкапы, ретро, демо...". Читается как "мы знаем, что у тебя слишком легкая жизнь, ублюдок, и уж ты нам поверь мы сможем её испортить".


Для меня стало принципиальным понять и объяснить всем, почему все эти синки бесполезны. Я нашел много аргументов.


У нас вот тут есть гит, гитхаб, рабочий чат, конфлюенс, в конце концов код и комменты в нем. Код ревью, ишьюс, жира. Ало, вам мало места, где обсуждать рабочие моменты? А, вам неудобно текстом. А почему не удобно? Может потому, что ты, осел такой, нихрена у себя в башке не можешь уложить, что именно хочешь сказать, и теперь хочешь поразмышлять вслух за счет моего времени? Может быть дело в том, что тебе на самом деле не надо ничего ни с кем обсуждать, и ты просто хочешь поизображать продуктивность?


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


Так может быть, ну я не знаю, СРАЗУ это записывать вместо созвона?


Это самое главное все, что было сказано на созвоне, и не записано не существует. Оно лежит только в памяти созвонщиков, которые могут забыть или уволиться. Или вообще запомнить по-разному и потом спорить до хрипоты, как оно было. Всякие брейнштурмы над серьезными проблемами могут сработать но не лучше, если делать это в переписке. А самое главное переписка то останется, мы всегда сможем вернуться к ней, и понять, как мы пришли к решению, и почему.


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


Я большой любитель засесть за работу на всю ночь и последнее, чего мне потом хочется, это проводить утренние эджайл ритуалы. Обычно, чем больше кода я пишу, чем больше и лучше работаю тем больше зверею от созвонов. Они или мешают мне работать, или мешают отдыхать, когда всю работу я уже сделал.


Я подумал ну наверное я не один такой. И стал спрашивать. Короче, я не нашел ни одного разраба из своих друзей, которому бы они нравились. Один из них он работает в сбере вообще от этой темы воспламенился и выдал самую гневную тираду за свою жизнь, а он обычно очень спокойный чувак.


Я не раз ловил себя на ощущении, что вот сидим мы третий час на созвоне пять разрабов, два неразраба. И все разрабы тупо ждут, когда это наконец кончится. Когда одному из нас задают вопрос, он переспрашивает потому что он нахрен не слушал. Все заняты своими делами, чтобы сделать за это время хоть что-то важное. Я кожей чувствую волну радости у своих братанов программистов, когда произносится: Ну все, вроде все вопросы решили, всем удачи!. Оооо да, давай чувак, удачи тебе, пока пока, счастливого рабочего дня, у меня нет никаких вопросов, я скажу тебе все что угодно просто закончи это.


Так вот, если многим программистам не нравится созваниваться, то как тогда вышло, что долбанные синкапы занимают такое место в нашей рабочей культуре?


Я нашёл три причины. Есть люди, которые плохо воспринимают текстовую информацию, и не могут без собеседника нормально сформулировать мысль. Это знаете, такой твой тимлид, который выдает "давай голосом" на любую мелочь, с которой тебе не повезло к нему прийти. Для таких людей у меня очень плохие новости им надо или переучиваться, или проваливать из цифровой индустрии. Разработка это про текстовое взаимодействие, и только так.


Есть люди, которые просто любят поболтать. Они самое меньшее из зол, потому что у меня всегда срабатывал такой лайфак: "Братан, если ты хочешь поболтать, звони после работы. Я возьму пивка, и мы обсудим все, что ты хочешь без давящей иллюзии, что мы решаем рабочие задачи". Если человек просто хочет поболтать с ним можно просто поболтать. Просто не надо делать это частью рабочих обязанностей людей в команде. Если у тебя в команде есть хороший друг, и вы любите программировать в паре флаг в руки. Когда созвон происходит по обоюдной инициативе, тут все в порядке. Но в моей практике, мне их всегда навязывали, и мой отказ созваниваться воспринимался как нежелание работать.


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


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


Я искренне ненавижу этих людей. Я их презираю. Как разраб я умею неплохо писать код на пяти языках, заниматься девопс задачами, тестировать, тестировать из кода, знаю кучу библиотек, фреймворков и рантаймов. Умею проектировать базы данных. Разбираюсь в межсетевом взаимодействии, алгоритмах, оптимизациях, управлении, мать его, проектами, обучении людей, и ещё много в чем ещё. Знаю предметную область процессов, которые автоматизирую. Мой минимум необходимых компетенций включает огромное количество вещей из смежных направлений потому что иначе я буду попросту бесполезным. Это в той или иной степени умеют вообще все программисты. А вот люди из IT, которые ни разу в жизни не удосужились написать простейший скрипт я не понимаю, что они тут забыли. Их непрошибаемое нежелание изучить хоть что-то техническое вызывает отвращение.


При этом, у меня нет проблем с тем, что от них нет никакой пользы в индустрии куча денег, и если кому-то они достаются буквально ни за что ну и бог с ним. Мне будет насрать, даже если им будут платить в десять раз больше чем мне. Кто-то хочет содержать бесполезных, тупых бездельников я могу с этим жить. Проблема в том, что бездельники знают о своей бесполезности, и чтобы скрыть её набивают свой рабочий день бессмысленными ритуалами. Это их защита нападением. Они убеждают всех вокруг, что люди, которые не любят созвоны и не пляшут по ритуалам на самом деле не хотят работать. Это чушь. Я убежден, что не умеют работать именно созвонщики.


А раз уж в индустрии ничего не работает без разрабов, они начинают набивать этими ритуалами и наш рабочий день. Джира, синкапы, ретро, планнинг покеры, обязательные отписки в чате в начале и конце рабочего дня, корпоративные мероприятия у этих болванов есть на это время, потому что они же больше ничего не делают.


А мы ещё должны писать код. Я устраиваюсь работать разрабом, потому что хочу писать код за деньги, и мне кажется, требовать, чтобы большая часть моей работы заключалась именно в разработке мое неотъемлемое право. Вы не можете нанять разраба, и заставить его с утра до вечера заниматься тестированием сколько бы вы ему не платили. Так почему тогда вы готовы заставлять программиста разговаривать и исполнять ваши ритуалы?


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


Последний год я не работаю в компаниях. У меня проектные работы, а на них от силы один созвон в неделю. Никогда еще я не был настолько продуктивным. Я делаю очень много, при этом у меня остается куча свободного времени, чтобы попилить пет проект, пописать статьи и поснимать свой подкаст. И мысль, что мне надо много сделать на работе вызывает радость, а не привычное отвращение. Ведь только от меня зависит, сделаю я все за час, или за сутки, и сделаю ли я больше чем надо в тот момент, когда необходимый минимум готов, я уже могу отключить телефон и уехать на остаток дня на реку потому что у меня гарантия мне никто не станет названивать. Удивительным образом такое положение вещей изменило мой подход к работе теперь я очень часто делаю больше, чем надо.


Если мои проектные работы кончатся, и мне придется опять работать в корпорации, которая лоббирует созвоны я просто ни черта, ничегошеньки, ни разу там не буду делать. Удалю с компа все IDE, поставлю кучу слаков, и с головой погружусь в работу. Если им нужен созвонщик, они его получат.


Смотрите мой подкаст
Подробнее..

Пример реального проекта на F

26.01.2021 18:12:26 | Автор: admin

В экосистеме Майкрософт, F# занимает место экспериментального языка, удачные концепты из которого, впоследствии, переносятся в C#. Вместе с тем, во многом благодаря сообществу, фаршик стал реальной альтернативой для прикладных проектов. Под катом описаны ингредиенты бэкенда, фронтенда, тестов, сборки и инфраструктуры проекта, полностью написанного на F#. Исходный код прилагается.

Диаграмма контейнеровДиаграмма контейнеров

SAFe

На выбор ингредиентов, определяющее влияние, оказал SAFe Stack. SAFe представляет собой шаблон dotnet CLI, в котором подобраны необходимые компоненты для гомогенной разработки SPA в связке с бэкэндом. Сайт проекта содержит много обучающих материалов и примеров.

SAFе облегчает старт для новичка. На первом этапе можно ничего не менять в шаблоне и сразу переходить к разработке функционала. По мере накопления опыта, вы, скорее всего, отойдете от SAFe.

Первая буква акронима - S - означает Saturn - идиоматический фреймворк над Giraffe, который, в свою очередь, функциональная обертка над Asp.net.

Вторая буква - A - означает Azure. Здесь мне было сразу не по-пути с SAFe, а тем, кто использует Ажур, как платформу, пригодится библиотека Farmer, которую пилят те же люди, что и SAFe.

Третья буква - F - означает Fable - транспайлер из F# в JavaScript - настоящая сдобная булочка в экосистеме фарша.

Бэкенд

Для создания API используется библиотека Fable.Remoting. Fable.Remoting скрывает абстракции веб-сервера. Типы, определяющие контракт, помещаются в общий для бэкенда и фронтенда файл (или сборку). Реализовываете API на сервере, все остальное (создание прокси, сериализацию, обработку ошибок, логирование) делает за вас библиотека. Помимо JSON, поддерживается передача бинарных данных.

Сейчас, имея такой удобный инструмент, как Fable.Remoting, я не вижу смысла тянуть в бэкенд колбасу Saturn - Giraffe - Asp.Net. Но, по историческим причинам, в проекте остался Giraffe.

Если нужно использовать спецификацию OpenAPI, можно посмотреть на GiraffeGenerator.

В качестве хранилища данных, в проекте используется NoSql база DynamoDB. За основу реализации слоя доступа к данным была взята библиотека DynamoDb.Ok. В ней используется идиоматический подход на основе монады Reader. В итоге, могу сказать, что этот подход мне не понравился. В будущем хотелось бы вообще не отвлекаться на код в слое доступа к данным. Есть идеи, возможно, об этом выйдет отдельная статья.

C реляционными базами из F# работать не приходилось. В чате сообщества замечал нарекания на ограниченную поддержку типов F# в Entity Framework и рекомендации использовать Dapper.

В прошлом году сообщество обогатилось серией годных статей по теме внедрения зависимостей: статья 1, статья 2, статья 3. Подход, базирующийся на Flexible Types, применяется и в данном проекте.

Для логирования используется библиотека Serilog, у которой есть расширение для Giraffe.

Для авторизации используется самописная реализация JWT.

Для связи с AWS частично используется дотнетовский AWSSDK, частично HTTP, так как SDK не покрывает весь функционал облака.

Фронтенд

Основа фронтендов на F# - Fable, Ваш код и большая часть стандартной библиотеки переводится в JS. Можно легко взаимодействовать с библиотеками JS. Существует множество обвязок (binding) для популярных библиотек, в т.ч. React и его компонент.

Для управления состоянием используется Elmish - реализация Elm-архитектуры. Рендеринг страницы производится с помощью Fable.React и Bulma.

Разработка фронта в этой экосистеме доставляет.

пример кода
let quizView (dispatch : Msg -> unit) (settings:Settings) (quiz:QuizRecord) l10n = [   br []   figure [ Class "image is-128x128"; Style [Display DisplayOptions.InlineBlock] ] [ img [ Src <| Infra.urlForMediaImgSafe settings.MediaHost quiz.ImgKey ] ]   br []   h3 [Class "title is-3"] [str quiz.Name]    div [Class "notification is-white"][       p [Class "subtitle is-5"][           match quiz.StartTime with           | Some dt -> str (dt.ToString("yyyy-MM-dd HH:mm"))           | None -> str "???"            if quiz.Status = Live then               str " "               span [Class "tag is-danger is-light"][str "live"]           br[]       ]        p [] (splitByLines quiz.Description)        if quiz.EventPage <> "" then           a[Href quiz.EventPage][str l10n.Details]    ]]

Отличное введение в экосистему - книга The Elmish Book.

Однако, фронтдендеры такие фронтендеры, связка Elmish + Fable.React + Boolma уже вышла из моды. В 2021 году, чтобы быть в тренде, вам нужно освоить Feliz + Fable.React.WebComponent + Material UI и рассмотреть альтернативу - Fable.Svetle. Моя бэкендер страдать.

Для взаимодействия с Aws, а именно с брокером сообщений в AppSync, используется библиотека Aws Amplify.

Тесты

Для проверки нефункциональных требований были реализованы нагрузочные тесты. Без использования сторонних решений (тянуть сюда JMeter показалось перебором).

Модульные тесты не писались. Не могу не отметить то чувство защищенности, которое дает система типов F#. Глупые ошибки обычно отлавливаются компилятором. За все время эксплуатации проекта, словил всего один мажорный баг на проде. В других проектах использовал FsUnit и expecto. Первый проще встраивается в инструментарий, второй гибче в синтаксисе, но, как по мне, большой разницы между ними нет.

Из прочих решений для тестирования, в моем списке на попробовать:

  • FsCheck - для тестирование на основе свойств

  • Canopy - фреймворк и DSL для тестирования UI

  • NBomber - для нагрузочных тестов

Сборка и развертывание

Для управления пакетами используется Paket. Для сборки используется скрипт Fake. Оба инструмента входят в шаблон SAFe. Судя по обсуждению, в настоящее время есть какие-то проблемы в запуске фейковых скриптов и, в будущем, сборка будет выполняться из консольного приложения. В таком случае, вообще не вижу смысла в фейке.

Для обновления инфраструктуры и развертывания используется AWS Cloud Development Kit. Поддержка F# не заявлена, но идет из коробки, вслед за C#.

Инструментарий

Хорошая новость. В SAFe сконфигурировано горячее обновление клиента и сервера в режиме разработки. К этому быстро привыкаешь и потом не понимаешь, как можно было работать по-другому.

Плохая новость. Инструментарий развивается, но он уступает тому, что есть в C#. Проект разрабатывается в VSCode с расширением Ionide. Подвисания, перезагрузки, регулярная необходимость удалять временные файлы - все это по-прежнему присутствует. Есть подозрение, что, в более крупных проектах, это может сильно испортить жизнь разработчику. Альтернативой Ionide является Rider. И там и там недавно вышли обновления, будем надеяться, что ситуация улучшится.

Помимо Ionide, используется расширение ILSpy, для того, чтобы понимать логику компилятора.

Также, работая с F# необходимо менять привычки отладки. Меньше пошаговой отладки, больше вывода в консоль. Использовать FSI.

Итого

По состоянию на начало 2021, F# пригоден для прикладных проектов небольшого и среднего размера. Для меня, преимуществами этого языка являются:

  • экосистема фронтенд-разработки,

  • система типов,

  • компактный синтаксис.

До тех пор, пока эти возможности не появятся в C#, при прочих равных, мой выбор будет в пользу фаршика.

исходный код проекта

Картинка-поощрение для тех, кто дочитал эту статью до конца.Картинка-поощрение для тех, кто дочитал эту статью до конца.
Подробнее..
Категории: Net , F , Fsharp , Fable , Elmish , Fake , Paket , Safe , Fable.remoting

В России плохо жить, даже если ты разраб. Но я все равно отказываюсь от релокейта

16.07.2020 18:14:38 | Автор: admin


Несколько лет назад я читал на Хабре про скандал разраба Андрея Адаменко с компанией Xored. Липкий кейс, компания не заплатила человеку деньги, и для меня сразу стал очевиден главный мудак в этой истории. Непонятно было другое. Адаменко наняли работать в новосибирском офисе компании, а он очень хотел поработать в пражском. За те же деньги. Кое-как уговорил их и уехал туда. В целом его право, но меня тогда очень задело другое неужели человек настолько ненавидит жить в РФ, что готов смотаться отсюда на любых условиях?

С тех пор я повзрослел и немного поумнел начал понимать желание уехать куда подальше. Когда живешь в Иванове с рождения, тебе очень многие вещи кажутся естественными. Взятки дпсникам, дерьмовые дороги, знакомые, которых посадили за полграмма гашиша в кармане. Мизерные, просто смешные зарплаты всяких врачей и учителей, абсолютное отсутствие намека на благоустройство, менты на каждом шагу, черные зарплаты, и тотальное неуважение к закону всеми и на всех уровнях.

Взрослые люди, у которых есть интернет, начали со временем узнавать все может быть иначе. Всякие условные журналисты могут это понимать сколько угодно они все равно никому во всем мире даром не нужны. А вот хороший разраб может в любой момент переехать куда угодно, и жить там заметно выше среднего.

Такое положение вещей культивировало релокейты. Сегодня релокация разрабов большая доля всех обсуждений в индустрии. Об этом пишут статьи, вокруг идеи завести трактор создаются сообщества, эйчары из релоцирующих компаний царской походочкой прогуливаются по рынку кандидатов. Многие, очень многие считают, что единственный логичный путь развития карьеры это свалить отсюда к херам собачим и никогда не возвращаться.

Сообщество снисходительно шутит над неудачниками, у которых слабый английский, мы все делаем вид, что статьи на русском мы в принципе не читаем. Россия, русские это все говно собачье. Индустрия в Америке, люди которые пишут комменты в коде на русском низший, вонючий сорт. Когда пишешь хорошую статью на хабр, люди недоумевают как так может быть, что это не перевод!? Попробуйте сказать какому нибудь русскоязычному коллеге из Нью-Йорка, что вы можете переехать туда, но не хотите засмеёт. Он не будет высмеивать желание остаться здесь он вам не поверит, и будет высмеивать то, что считает ложью.

Они говорят мне эй, дебил, ты живешь в дерме, жрешь дерьмо, и дерьмом же запиваешь. Вас трахает ваше же правительство, а вы с этим ничего не делаете. Ваше образование жалкий косплей провинциального колледжа из США. Ваша инженерная культура И так сойдет. Вы не нация, а жалкие придурки, живущие совковыми иллюзиями, что когда-то были великими.

А я сыт по горло стыдом за то, что я русский.

Русские инженеры построили много очень крутых проектов один JetBrains чего стоит. Я не говорю что мы лучше всех, но я точно знаю, что мы умеем быть не говном. У нас есть места, где учат программировать действительно хорошо. Я напрочь рвусь, когда вижу очередную переводную статью от рувдс, про так как отрендерить список в js. Да у нас тысячи фронтендеров, которые напишут лучше и глубже просто, ну не знаю, чуть-чуть помоги им.

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

Но наша страна и правда набита говном под завязку, иногда оно воняет настолько сильно, что никакая идеологическая верность не поможет тебе закрыть на это глаза. Когда мусора в очередной раз помогают жирным ворюгам отжимать условный нжинкс, когда космонавты фигачат очередную либераху на митинге, когда ты десятый раз за год чинишь подвеску на своей тачке от самообладания и стройной теории, что Россия очень хорошая, ничего не остается.

Я несколько раз был за границей, и мне там безумно понравилось. Но даже за три туристические недели я начинал безумно скучать. Это не такие штуки, которые хорошо раскладываются на логические доводы, я толком не могу объяснить себе в чем дело, но жить здесь, наша культура, наши люди, мои близкие, в конце концов даже сраный климат все это для меня очень значимо. И когда я рассекал по зеркально ровной трассе в Испании, я не переехать в Испанию хотел я хотел чтобы у нас были зеркально ровные дороги.

Меня немного передергивает от обиды, когда мне предлагают релокейт в Данию с такой же зарплатой, как у меня сейчас в России. Они или думают, что я настолько тупой, что не понимаю разницу в цене жизни, или считают, что я должен лизать им ботинки за то, что перевозят грязного варвара в страну белых людей. Или вообще ничего плохого не думают, но ничего страшного я и сам за них обо всем подумал. В наиболее негативном ключе, потому что я заколебался испытывать стыд за то, что родился здесь, и не хочу делать вид, что я ненавижу это место.

Это рождает довольно простую мысль. А что если не бежать от говна, а пытаться его уменьшить? Я уверен, в любой стране мира хватает своих проблем. И я не обесцениваю да, скорее всего проблемы с неправильным государством в Новой Зеландии != проблемам с отвратительно людоедским государством у нас.

Небинарность хорошего и плохого очевидная для всех вещь. Но мы все равно постоянно об этом забываем. В России плохое государство, в Германии плохое государство. Мой код плохой, и код человека, который только вчера прочитал свой первый учебник про джаваскрипт тоже плохой. Но его код в сто раз хуже моего (ну, я надеюсь). И слово плохой становится недостаточно емким, чтобы описать положение вещей. Если кто-то скажет про наш код, что он плохой он обесценит весь мой опыт, и весь труд, который я годами совершал в этой индустрии. Это не круто.

Я пришел к принятию. Есть люди, которые не готовы тратить свою жизнь на войну со злом здесь и сейчас они хотят хорошенько пожить в теплой и уютной стране, где всем не насрать уже сегодня. Где твои инициативы по улучшению жизни не глушат дубинками, и где ты не рискуешь усесться на бутылку за твит. Осуждать таких людей, или осуждать тех, у кого и в РФ все более менее нормально плохая идея, потому что никто не обязан жить свою жизнь так, чтобы ты считал это православным. Те, кого душит идея жить в этой стране, они смогли пройти сложный квест и переехать мое уважение. Но, люди, давайте так уважайте и мое право не хотеть этого.

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

Есть люди и организации, которые каждый день занимаются тем, что называют борбой с режимом. Их не так много, и выбор для поддержки не особо широкий, а результаты их работы удручают. Ну протащили они пару своих депутатов в деревенскую думу, ну и дальше что? Ждем-то, что завтра мы проснемся в прекрасной России будущего, виновные будут люстрированы, а хорошие люди начнут демократично управлять страной.

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

Но просто поверить в чью-то идею не сработает. Я пессимист, и вообще не верю, что когда-нибудь увижу большие перемены в лучшую сторону в этой стране. Мне сейчас 26, у меня двое детей, я перестал расти и развиваться, и я не верю, что скучное течение моей жизни когда-нибудь изменится. Скорее всего, вектор задан, и я буду ему следовать, пока не сдохну, такой же как сейчас, не способный ни к анализу своих действий, ни к каким-то переломам и открытиям в сознании, ни вообще к чему бы то ни было значимому.

Может быть, я буду умирать в такой же России как сегодня, может быть я сломаюсь, и свалю в Тайланд, а может быть, совсем крошечный шанс, что я буду сидеть, и объяснять внукам, что офигенное место, в котором они растут, стало таким потому, что я когда то решил его таким сделать.

У меня есть подкаст, и это, в сочетании с хабром единственная возможность донести до кого-то идеи, которые не дают мне покоя. Я пошел на гигантский репутационный риск и позвал Михаила Светова в подкаст про разработку. Потом пошёл на ещё больший риск, когда приперся рассказывать об этом на хабр.

Риск потому что разрабы часто аполитичные, а Светова, кроме того, принято хейтить в либеральной тусовочке, потому что в центре его повестки не стоят права меньшинств. В итоге все недовольны.

Но я уже говорил, в моей жизни нет ничего особенно ценного. Моя репутация на самом деле ни черта не стоит. Я приходил сюда и писал в открытую, что не хочу работать на работе, что мне наплевать на продукт, что я ничего не знаю и не умею. Статьи читали сотни тысяч человек и я уже хоронил свою карьеру. А потом получал ворох офферов и понимал да всем насрать.

Кейсы со сломанными процессами в разработке, синдромом самозванца, уродской культурой менеджмента в индустрии оказались достаточно болезненными для меня, чтобы рискнуть, и написать об этом. Кейс с ситуацией в стране в сто раз важнее.

Я не переехал в Данию, не переехал в Тай, не переехал в Штаты и меня каждую секунду мучает мысль, что я идиот, раз так поступил. Но я все еще могу это сделать когда угодно, поэтому каждый день вынужден бороться с соблазном. И меня бесит, что этот соблазн вообще существует. Бесит, что борьба с ним кажется бессмысленной ведь если даже останусь я, точно уедут дочери.

Мне хочется, чтобы желание остаться дома было нормой, а не геройством.
Смотрите мой подкаст
Подробнее..
Категории: It-эмиграция , F

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru