Pull to refresh
41
Роман Лиман@kagetoki

F#/C# developer

10
Subscribers
Send message
Вы добавляете логирование в свойства ДТО/моделей? А что вы предлагаете делать, когда мыльник пустой пытаются присвоить?

Зачем жить с половиной слабореализованных фич, если можно получить полный набор?
Кроме того, про DU в сишарпе вообще пока ничего не слышал. Expression-based он тоже вряд ли станет когда-нибудь, верно? Обе эти фичи сильно способствуют стабильности и самодокументируемости кода.

Разные способы есть. Если email по бизнес логике обязателен, то, например, можно вернуть ошибку с помощью DU еще до создания этого экземпляра, но мне нравится вот такой подход:


module Email =
    type EmailAddress =
        private
        | ValidEmail of string
        | InvalidEmail of string

    let ofString = function
        | "validEmail" -> ValidEmail "validEmail"
        | invalid -> InvalidEmail invalid 

    let (|ValidEmail|InvalidEmail|) = function
        | ValidEmail email -> ValidEmail email
        | InvalidEmail email -> InvalidEmail email

open Email

let invalid = Email.ofString "invalid"
let valid = Email.ofString "validEmail"

match invalid with
| InvalidEmail invalid -> printfn "invalid was InvalidEmail %s" invalid
| ValidEmail valid -> printfn "invalid was ValidEmail %s" valid

match valid with
| InvalidEmail invalid -> printfn "valid was InvalidEmail %s" invalid
| ValidEmail valid -> printfn "valid was ValidEmail %s" valid

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

Видимо, я неточно выразился.
Во-первых, F# точно не является серебрянной пулей: система типов в хаскеле, например, мощнее, а в C# есть nameof, partial classes, которые делают его более удобным для генерации code behind. Я, например, не знаю как F# работает с WPF — не пробовал.
Во-вторых, я использовал упрощенные примеры, просто потому что так проще писать статью.
Да, в F# можно объявить обыкновенный мутабельный класс, и тогда компилятор не защитит это поле в рекорде или в DU. Мой поинт не в том, что F# покроет все магической защитой, а в том, что F# позволяет легко создавать и работать с неизменяемыми структурами, в отличие от C#. Да, массив все еще изменяемый, но из коробки в F# есть неизменяемые List, Set & Map.
По поводу случайного инта — я, конечно, ни в коем случае не ожидаю, что кто-то будет случайным образом пихать аргументы в индексатор, в конце концов, проверка длины массива — одно из первых правил, которое выучивает юный программист. Я лишь добавил это как демонстрацию того, насколько функция далека от тотальности. Тем не менее, несмотря на то, что мы научнены жизнью и делать так не будем, факт остается фактом: функция написана так, что бОльшая часть диапазона входных параметров вызовет исключение.


Что касается примера с if/else, я знаю про тернарный оператор, да. И про var, я без всякой задней мысли поставил там int, ошибся, сорян. Опять-таки, пример упрощен, и я там говорю про добавление веток в будущем. Можно сделать вложенные тернарные операторы, но я так делать не люблю из-за плохой читаемости.
Давайте сделаем пример более боевым:


let myResult =
         if condition then
             let a = myFunc arg1 arg2
             let b = myFunc2 arg3
             a + b
         elif condition2 then
             myFunc arg4 arg5
         else
             myFunc2 arg3         

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

Во-первых, есть elmish xamarin. Там FP-UI. Я не говорю, что вам нужно его выбрать или что это единственный вариант, но примеры посмотреть уже можно.

Во-вторых, независимо от того, веб у вас или десктоп, в приложении есть бизнес-логика. Ее удобней делать на чистых функциях, как я и написал в статье. Потому что это стабильней и легче тестировать. Сам слой презентации вы можете описать на C#, если вам так больше нравится. Они с F# нормально уживаются даже в рамках одного солюшена.
Почитайте про Elmish. Для примера можно взять этот проект

Сумбур какой-то.


Уже почти не важно на чем вы пишете: Python, Java, Scala. Код на любом из этих языков можно обернуть в образ и поставлять контейнером.

С одной стороны вы говорите, что не важно на чем писать, с другой стороны говорите, что ФП может быть помехой. Если вы уже используете JVM или .net, вся экосистема вам уже доступна, все фреймворки и библиотеки к вашим услугам. Разница только в сложности поддержки кода, и для многих задач ФП обеспечит более легкий в поддержке код, особенно если речь идет про потоковую обработку данных и/или конкарренси.


В С# — это, например, Entity Framework и .Net. В Java — это Hibernate и Spring. И почти каждый фреймворк нацелен на работу в привычном нам ООП стиле. Почти всегда эти решения имеют изменяемые состояния и совершенно не пригодны для работы с чистым ФП.

То, что, например, EF работает с мутацией наших моделей, все еще не означает, что мы не можем его использовать из F#. Популярное решение — impure/pure/impure sandwich. Сделайте модели, которые будет мутировать EF, они все равно даже в ООП обычно сразу мапятся в другие модели, и наш собственный код их никак не мутирует. Собственно, конкретно в F# вы можете использовать рекорды с аттрибутом [<CLIMutable>], который позволит десериализацию, но ваш код мутировать эти объекты не сможет.


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

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


Остается всего одна проблема. Что с ним делать дальше? Как настроить процесс автоматизации? Как настроить “умную” балансировку? Как другим сервисом найти ваш? На эти вопросы ответа у вас может просто не быть!

Я не понял, как это связано с тем, на каком языке написан сервис. С момента, как вы его захостили и выставили публичный апи, всему остальному миру плевать, на чем внутри написан сервис, он использует его как черный ящик со стандартным протоколом коммуникации. Проблемы балансировки и service discovery тоже непонятно какое отношение имеют к парадигме программирования. Если уж на то пошло, есть Azure, который предоставляет автоскейлинг, например, для Azure Functions. И можно писать AF хоть на С#, хоть на F#, хоть на JS, не побоюсь этого слова.


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

Кейс редкий, но допустим.
Во-первых, коль скоро мы говорим про F#, тут можно без каких либо проблем применять ООП подход: вам доступны и традиционные мутабельные ссылочные типы, и Nullable, и куда более удобный Option<T> (который, кстати, тоже тип-сумма). И вы можете задизайнить в привычной манере.
Во-вторых, если концепция плохо работает на каком-то радикальном случае, это не значит, что концепция плоха в принципе. Любой инструмент имеет смысл в рамках решения задачи, и если конкретная задача решается с его помощью плохо — всегда можно выбрать другой.
В-третьих, кейс с большим количеством полей можно вообще спроектировать принципиально по-другому, например, как массив объектов нашего типа-суммы (в котором 10 кейсов), и валидировать, что длина больше 0. А лучше вообще завернуть это в еще один тип-сумму
type MyInfo = | Invalid | Info of MyDU list


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

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


И напротив, когда дизайн как в статье, на Discriminated Unions (они же типы-суммы и размеченные объединения), он говорит разработчику, не знакомому с кодом, что одно из полей обязательное. И добавление нового кейса без обязательных полей уже в явном виде говорит, что мы меняем бизнес логику

Если убрать дилей вообще, выглядит это так:



Если поставить 100 мс, будет так:


Все просто: если разработчик позиционирует себя в профессиональном смысле как C# developer, то независимо от того, знает ли он SQL/NoSQL/JS/Azure и тд, он все равно большую часть рабочего дня пишет код на C#, и подавляющее большинство его задач тесно связаны с C#. И если все то, что у него отнимает больше всего рабочего времени, можно решить проще, быстрее и надежнее, то ухватиться за эту возможность в долгосрочной перспективе будет выгодно и ему, и бизнесу.
Тем более, что это даже не будет радикальным изменением, как например, переход на хаскель или го. Это та же платформа с той же инфраструктурой. C# и F# можно даже в одном солюшне сочетать, так что плавный переход возможен даже на монолитных приложениях.
И освободившееся от багфикса и рефакторинга время можно будет потратить в том числе на изучение других технологий.
Я не делаю вид, что этого не надо и не говорю, что F# идеален. Да, было бы круто иметь инструменты вроде решарпера (я, кстати, им и в C# не пользуюсь, но все равно), было бы здорово, если бы debug tools были столь же хороши или даже лучше, чем в C#. На данный момент в F# нет оператора `nameof`, к сожалению.
Я говорю, что несмотря на отсутствие/отставание этих инструментов эффективность решения бизнес задач на F# все еще выше, чем на C#.

Фейсбук активно использует хаскель для борьбы со спамом и очень этим доволен. Тут уже несколько раз вспоминали про Вагифа, а ведь у них уже было все написано на C# и они все равно пошли на такой риск для бизнеса, как не просто все переписать, а еще и переписать на другом языке, к тому же, менее популярном. И остались довольны. Насколько я знаю, шведская компания Record Union тоже собиралась переписывать то, что у них есть, с C# на F#. Rust на stackoverflow является самым любимым программистами языком.


Если захотите найти аргументы, чтобы не учить F# или ФП в целом, вы их найдете. Только зачем?

То есть, возможность написать запрос к google API в 7 строк — это хорошо, но наличие библиотеки googleAPI — лучше.

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


У F# есть какие-то «инфраструктурные» преимущества?

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


Для тех задач, которые может закрыть F# — у разработчика уже есть C#

Верно. Но на F# эти задачи можно решить проще, быстрее и надежнее. Если это для вас не аргумент, тогда мне больше нечего предложить.

Главным образом это применимо к Discriminated Unions. Вот тут можно глянуть пример goo.gl/V8pozz
Даже после чистки всех автосгенеренных аттрибутов и форматирования все равно счет строк идет на сотни

Information

Rating
Does not participate
Location
Сингапур, Сингапур, Сингапур
Registered
Activity