×

Мультивсесвіт C# — сингулярність мови програмування

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

Своє знайомство з .NET і C# я почав приблизно у 2007 році. Я тоді навчався на 4 курсі і на той момент вже мав досвід роботи з мовами програмування C++ і Delphi. До того ж у мене був навіть комерційний продукт, написаний на Visual C++, який цілком непогано працював.

Але вже тоді мені було тісно у рамках, які ставили як Delphi, так і C++, я почав шукати альтернативи. Попрацював трохи з різними мовами і технологіями, а коли дібрався до C#, то відразу ж зрозумів — ось інструмент, який повністю відповідає моїм запитам і з яким комфортно і зручно працювати. З того часу я активно стежу за всім, що відбувається з платформою .NET та мовою C#.

У всякому разі, мені так здавалося донедавна, поки мені не потрапили на очі деякі публікації, з яких я дізнався, що крім звичного і знайомого нам C#, існує ще кілька «паралельних реальностей», де все начебто так само, як і у звичному нам всесвіті, але трохи інакше. Ось із цими альтернативними реальностями я і пропоную познайомитися трохи ближче.

Від редакції: стаття публікується в перекладі українською мовою зі згоди та за узгодження з автором, оригінал тексту можна прочитати тут.

Передісторія

Спочатку нам доведеться повернутися в часі приблизно на 20 років. Саме у 2003 році, у надрах Microsoft Research (це дослідницький підрозділ корпорації Microsoft, практично секретна лабораторія) розпочали роботу над проєктом Singularity — високонадійною операційною системою, в якій мікроядро, драйвери пристроїв та програми написані на керованому коді.

У статті «Singularity: Rethinking the Software Stack» співробітники Microsoft Research Джеймс Ларус і Гален Хант пишуть про Singularity так:

«Працюючи над проєктом, ми створили нову операційну систему, нову мову програмування та нові інструменти перевірки програмного забезпечення. Операційна система Singularity включає нову програмну архітектуру, що базується на програмній ізоляції процесів.

Наша мова програмування Sing# є розширенням C#, що забезпечує підтримку комунікаційних примітивів ОС, а також підтримку системного програмування та факторизації коду».

Історія створення операційної системи Singularity неймовірно цікава і заслуговує на окрему статтю. Але в цьому випадку мене зацікавила фраза «наша мова програмування Sing#». Я ніколи раніше не чув про таку мову програмування і мені стало цікаво дізнатися про неї більше. Стаття у «Вікіпедії» про цю мову виявилася дуже короткою і була представлена ​​лише у двох мовних версіях — російській та німецькій. Попри малий обсяг інформації, там виявилося дещо цікаве — у картці опису мови вказувалося, що на Sing# вплинула ще одна, невідома мені досі мова — Spec#. Виходить, що у нас вже є два відгалуження від C#.

Оскільки Spec# є основою для Sing#, то шукати детальнішу інформацію я почав з неї. Команда Microsoft Research так характеризує цю мову:

«Spec# — це формальна мова для контрактів API, яка розширює C# конструкціями для ненульових типів, передумов, постумов та інваріантів об’єктів.»

Про Sing#, згідно з публікацією «An Overview of the Singularity Project», зазначено таке:

«Sing# є розширенням мови Spec#, розробленої в Microsoft Research. Sing# розширює цю мову підтримкою каналів та низькорівневих конструкцій, необхідних для системного коду.»

Нагадаю, що йдеться про розробки, які велися приблизно з 2003 року по 2015 рік. Тобто з того моменту пройшло досить багато часу, але концепції, закладені в ті мови програмування, і зараз виглядають дуже прогресивними. Варто віддати належне похмурим геніям із Microsoft Research — вони не лише не засекретили інформацію про ці розробки, вони навіть зібрали та опублікували велику кількість матеріалів на сайті проєкту.

Вивчати статті та публікації можна досить довго — поки що я зміг прочитати (а скоріше навіть переглянути) лише малу частину з цих матеріалів. У процесі ознайомлення зі статтями мені стало цікаво: а чи мови з «паралельних реальностей» вплинули на C# і .NET, якими ми їх знаємо сьогодні?

Провівши невелике дослідження, я виявив, що цей вплив безумовно є. Хоча і Sing#, і Spec# не отримали подальшого розвитку, успішні ідеї справді були згодом запроваджені в C# — якісь раніше, якісь пізніше.

На сьогодні ми точно можемо зробити висновок, що низка концепцій була реалізована під час роботи над експериментальними інкарнаціями мови:

  • Code Contracts;
  • Non-null reference types;
  • readonly / pure methods.

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

Code Contracts

Проєкт Code Contracts є відгалуженням Spec# і забезпечує спосіб визначення передумов, постумов та інваріантів об’єктів у коді:

  • передумови є вимогами, які мають бути виконані при вході в метод або властивість;
  • постумови описують очікування на момент завершення роботи коду методу чи властивості;
  • інваріанти об’єкта описують очікуваний стан класу, що перебуває в нормальному стані.

Переваги, які пропонує використання Code Contracts:

  • поліпшене тестування: перевірка контрактів на етапах як компіляції, так і виконання;
  • інструменти автоматичного тестування: Code Contracts можуть використовуватися для генерації краще осмислених тестів, відфільтровуючи аргументи тесту, які не задовольняють передумов;
  • статична перевірка: статичний аналізатор може визначити порушення контрактів без запуску програми. Він перевіряє неявні контракти, такі як нульові посилання та межі масиву та явні контракти;
  • довідкова документація: генератор документації доповнює наявні файли документації інформацією про контракти.

Зазначу, що на сьогодні Code Contracts вже не підтримуються в .NET 5 і новіших версіях платформи, тепер замість них пропонується звернути увагу на використання nullable reference types.

Якщо ми подивимося на специфікацію Spec#, то побачимо таке визначення:

«Одним з основних елементів специфікації в Spec# є метод. Кожен метод може включати передумову, яка описує умови, за яких виклик метода дозволений, і постумову, яка визначає умови, за яких повернення метода дозволене. Отже, реалізація метода може припускати, що передумова дотримується при вході, і метод, що викликається, може припускати, що постумова дотримується при поверненні. Цю угоду між методами, що викликаються, і реалізацією, часто називають контрактом метода.»

Таким чином, ми можемо зробити висновок, що концепція контрактів є однією з основних для Spec# та знайшла своє органічне продовження у реалізації code contracts.

Non-null reference types

У восьмій версії мови C# Microsoft запровадила поняття non-nullable references, які дозволяють розробнику гарантувати, що тип посилання не може бути нульовим. Це досягається завдяки перевіркам на етапі компіляції, що своєю чергою дозволяє створювати простіші та надійніші програми.

Концепція non-null reference types також сягає своїм корінням в Spec#, як і концепція Code Contracts. У Spec# оголошення non-null reference виглядало так:

class Student : Person {
    Transcript! t;
    public Student(string name, EnrollmentInfo! ei)
       : base(name) {
        t = new Transcript(ei); 
    }
    ...
}

Варто зазначити, що Ентоні Хоар, один з творців мови програмування ALGOL W, заявляє, що концепцію null reference запропонував саме він. І сьогодні він вважає, що це була «помилка на мільярд доларів»:

«Я називаю це своєю помилкою на мільярд доларів. Це був винахід нульового посилання у 1965 році. На той час я розробляв першу комплексну систему типів посилань в об’єктноорієнтованій мові (ALGOL W). Моя мета полягала в тому, щоб гарантувати, що будь-яке використання посилань має бути абсолютно безпечним, з автоматичною перевіркою компілятором. Але я не міг устояти перед спокусою вказати нульове посилання просто тому, що це було так легко реалізувати. Це призвело до незліченних помилок, уразливостей та системних збоїв, які, ймовірно, завдали мільярдів доларів збитків за останні 40 років.»

Думаю, що з Тоні погодяться всі розробники, кому хоч раз доводилося мати справу з null pointer exception.

«Щоб зробити використання non-null типів ще зручнішим для програмістів, які переходять на Spec# із C#, Spec# передбачає виведення non-nullity для локальних змінних. Це виведення виконується у вигляді аналізу потоку даних компілятором Spec#.» (звідси)

readonly / pure methods

У дев’ятій версії мови C# було реалізовано можливість оголошення структур лише для читання — readonly struct. Структура, оголошена з ключовим словом readonly гарантує, що жоден з її елементів не змінить стан структури. При цьому ключове слово readonly може застосовуватися як до всієї структури, так і її окремих полів, або методів.

public readonly struct Coords
{
    public Coords(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double X { get; init; }
    public double Y { get; init; }

    public override string ToString() => $"({X}, {Y})";
}

Така поведінка була реалізована в Sing# через атрибут Pure: метод (функція, що належить класу) називається чистим і оголошується таким атрибутом Pure у разі, якщо він повертає значення без зміни стану. Чисті методи не повинні мати побічних ефектів:

[Pure]
public int MagicNumber() {
  return 3 + gt.StringCount(); 
}

Тут я звернув би увагу на ще один цікавий момент: функціональне програмування іноді розглядають як синонім чистого функціонального програмування, підмножини функціонального програмування, яке розглядає всі функції як чисті функції. Віднедавна можна побачити, як активно концепції функціонального програмування входять і досить органічно вбудовуються у звичні нам об’єктноорієнтовані мови.

Висновки

Цілком припускаю, що список прикладів впливу Spec# і Sing# на C# далеко не повний і, ознайомившись з матеріалами уважніше, ніж я, ви знайдете й інші схожі концепції мов програмування. А щоб вам було простіше займатися дослідженнями, я додаю список корисних матеріалів:

👍ПодобаєтьсяСподобалось7
До обраногоВ обраному6
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Цікава стаття, але як би не одне але.
На момент виходу С# не був ніяким прогресивним технічним кроком, а майже повним плагіатом Java.
І навіть за 20 років розвитку C# не вдалося витіснити Java з першого місця. Тобто банальний листок паперу з ліцензійною угодою використання Java мав в десятеро більший вплив на створення С# ніж всі ті
засекречені дослідницькі лабораторії всі разом взяті.

Он был похож, но с версии 1.0 был технически продвинутей Java.

То був більш маркетинговий крок. Навіть зараз неможливо визначити чий менеджед код працює краще. В обох віртуальних машинах використані одні і тіж оптимізації

шарп на данный момент превосходит во всем джаву, поддержка, компьюнити, функционал. А те кто рисует графики рейтинга не шарит прост

шарп на данный момент превосходит во всем джаву, поддержка, компьюнити, функционал. А те кто рисует графики рейтинга не шарит прост

А що, правило п’ятниці вже не працює ?
Чому цей пост запостили в середу ?

ниче не понел, но перед знаком вопроса пробел не надо

ты на ходу ответы придумываешь или берешь паузу?)

Настав час сказочних *** архітекторів.
Чи зможеш навести хочаб один приклад з практики, де на проект вибирали поміж .NET та Java, та .NET виграв с формулюванням що на ньому код буде швидше працювати чи жерти меньше памяті.
За багаторічний досвід я зустрічав лише вибір, коли ключовий момент був кросплатформеність, наявність специфічних бібліотек під мову, чи досвід наявних розробників.
І ніколи я не зустрічав, що хтось когось переконав бо в секретних лабораторіях створили якогось кіллера, що насовує іншому за комірці.
Це дві майже однакові технології
youtu.be/sJC3JsvhM9o

Чи зможеш навести хочаб один приклад з практики, де на проект вибирали поміж .NET та Java, та .NET виграв с формулюванням що на ньому код буде швидше працювати чи жерти меньше памяті

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

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

А ось тобі і справжня причина створення C#

ru.m.wikipedia.org/wiki/Visual_J+

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

Тобто поки гіки ганяли лисого на жіт компіляцію в сінгуляріті лабараторіях, що дає майже +5% до перформанцу та −80% до кросплатформеності, справжню долю С# вирішували юристи, а не гіки в далекому 2004 році.

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

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

Яка мотивація який результат, що ти верзеш. Топіки по психології та лічностного роста в іншому місті.
Тут обговорюють створення С# та події що мали на це вплив. Не більше і не меньше.

ты сильно в сторону ушел

Отож як я казав. Сказочний ***.

Так и что, понятно, что дешевле им было его не делать, а взять Java.
Но вышел сбалансированный язык, в конечном счете, который стал кросс-платформенным.
Если сравнивать языки и встроенные API, я честно не вижу никаких плюсов у Java, особенно сейчас спустя 20 лет.

который стал кросс-платформенным.

Не став. 15 років його пиляли, просравши кілька проектів типу Моно та Віндовз фон, і тільки з кор номінально його зробили кросплатформеним. Але навіть зараз по факту Джава всеосяжно охоплює і мобайл і різну побутову техніку і є дійсно кросплатформеною, а кор про таке ще буде років 10 мріяти.

Так, шарп відома мова, але як то кажуть результат на табло. Пройшло 20 років і він не обігнав джаву. І це при тому що на джаву взагалі забили болт та роками її не оновлювали.

С Mobile ситуация специфическая, я так понимаю под iOS на Java не попишешь особо? А то что Android базируется на JVM, это совпадение. Насчет embedded не интересовался.

Андроїд не базується на JVM там Dalvik

Не став. 15 років його пиляли, просравши кілька проектів типу Моно та Віндовз фон

Подивіться який відсоток ігор створюється на Unity перш ніж казати таку діч.

Докази чого? Що це топ 5 мова? Те що чверть ігор (а це той ринок де дійсно необзідна крос-платформа) пишуть на Unity?
6sense.com/...​t,share in the same space.

Ти мабуть переплутав домен.
Для різних ігроманів, яких в загальному сегменті айті може від сили 15-20%
створено спеціальний домен
gamedev.dou.ua
Всім іншим що там і як там роблять в іграшках не цікаво від слова зовсім.
Це як би я приперся з темою embedded в цю тему і могутньо довів що С++ набагато популярніший за С# та Java

В обох віртуальних машинах використані одні і тіж оптимізації

Хм, у мене є код, який 1:1 на Java працює разів в 5 швидче ніж на C#. Парсінг SIP, як належить, на класах символів.

Хм, у мене є код, який 1:1 на Java працює разів в 5 швидче ніж на C#. Парсінг SIP, як належить, на класах символів.

Не може такого бути.
Всім відомо що шарп був створений в суперсекретних ректальних сінгуляріті лабораторіях і в порівнянні з джавою жіт компіляція дає прирост в 100500 разів. Не читають єрєтікі рекламних мс брошюр.

Я не знаю чи

В обох віртуальних машинах використані одні і тіж оптимізації

Але твердження типу

Хм, у мене є код, який 1:1 на Java працює разів в 5 швидче ніж на C#

звучать доволі переконливо, щоб повірити під чесне слово.

Я не знаю чи

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

звучать доволі переконливо, щоб повірити під чесне слово.

ну а то вообще бред, я могу тоже самое сделать в обратную сторону, доказательство на основе частного случая невозможно

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

Есть F# для чудовищно узкого круга задач с параллелизмом и асинхронностью. Таких задач на галере не найдешь за всю карьеру.

в F# немає монад. а як тільки ви зрозумієте, що таке монада, для вас всі ці лямбда проперті і свічкейси стануть іграшками.

у чому для вас принципова різниця між LINQ та монадами?

Ну... наприклад, як за допомогою LINQ написати щось на кшталт вирішення задачі 8 ферзів?

import Control.Monad
import Data.List (delete)
queens n = map fst $ foldM oneMorequeens ([],[1..n]) [1..n]  where
  oneMorequeens (y,d) _ = [(x:y, delete x d) | x <- d, safe x]  where
    safe x = and [x /= c + n && x /= c - n | (n,c) <- zip [1..] y]

в LINQ теж є фолд

 
        var solutions = Enumerable.Range(1, n)
            .Aggregate(new [] { (Enumerable.Empty<int>(), Enumerable.Range(1, n)) },
                (acc, i) => acc.SelectMany((state) =>
                {
                    var (arr, d) = state;
                    return from x in d
                        where !arr.Select((c, _) => x == c + n || x == c - n).Any() 
                        select (arr.Concat(new[] {x}), d.Except(new[] {x}));
                }).ToArray())
            .Select(solution => solution.Item1)
            .ToList();

Ну... я спробував перевірити:

int n = 8;
var solutions = Enumerable.Range(1, n)
    .Aggregate(new[] { (Enumerable.Empty<int>(), Enumerable.Range(1, n)) },
        (acc, i) => acc.SelectMany((state) =>
        {
            var (arr, d) = state;
            return from x in d
                   where !arr.Select((c, _) => x == c + n || x == c - n).Any() // Safe(x, state.Item1)
                   select (arr.Concat(new[] { x }), d.Except(new[] { x }));
        }).ToArray())
    .Select(solution => solution.Item1)
    .ToList();

Console.WriteLine(solutions.Count);

Виводить нуль. Навіть якщо поставити n = 32, то ми моментально отримуємо нуль. Тобто код робить щось не те.

Але перевага монади тут полягає
більше у тому, що ми отримуємо backtracking перебір for free. Мені це не дуже очевидно з цього коду. Мені здається, що ви плутаєте монади та аплікативні функтори. Так, аплікативний функтор це монада, але зворотне не вірно.

Але перевага монади тут полягає
більше у тому, що ми отримуємо backtracking перебір for free. Мені це не дуже очевидно з цього коду.

А мені не дуже очевидно з Haskell коду, чи той бектрекінг працює так я треба для цієї задачі, тобто ферзі рекурсивно розставляє від центру дошки до країв, або як хаскелісту було зручно писати код.
Тому що для великих дошок ця еврістика дає виграш приблизно з безкінечності до кількох секунд.

там помилка в алгоритмі буде більше часу спробую выправити — ціль була показати, що навіть у С# є building blocks для вирішення задачі у функціональному стилі.
backtracking треба самому реалізовувати — але це і зрозуміло, що на С# для control flow є інший інструментарій, це в хаскелі власне без монад його не зробити ніяк(це скоріше недолік, аніж перевага).

Ми розмовляли не про функціональний стиль, ми розмовляли про монади. Мій поінт був у тому, що монада [] надає тобі відразу backtracking, плюс приклад, який це демонструє.

Також мені не дуже зрозуміло, що таке control flow. Ви про побічні ефекти? Ну тоді що поганого у тому, щоб обернути їх у монаду?

Мій поінт був у тому, що монада [] надає тобі відразу backtracking, плюс приклад, який це демонструє.

"Хаскєль как ламборджіні в дєрєвнє, нємнога падрачіл і пашол работать на тракторє"©

а як щодо цього?

import Control.Monad.Fix
import Data.IORef

data Node = Node Int (IORef Node)

mknode = mfix $ \p -> do
  p' <- newIORef (Node 0 p)
  putStrLn "node created"
  return p'
взято звідси elvishjerricco.github.io/...​adfix-is-time-travel.html

Господи, какое адище. LINQ это классный инструмент, если очень в меру в пару строк. Я представляю какой «кайф» кому-то сидеть и разгребать этот мусор глазами. А дебажить это все ... мммм

А дебажить это все ... мммм

ФП тому і ФП що або воно працює з першого разу так як треба, але не працює взагалі. Якщо набив руку, то зазвичай працює, дебажити не потрібно. Тобіж SQL select подебажити не хочеться ?

Ну... якщо брати pure functional programming, то зазвичай або не компілюється, або працює.

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

В академічному тлумачному словнику української мови є це слово.

тут не все академики, в чем преимущество использования этого слова, которого при чем еще фиг выговоришь, между его аналогами?

Ви що, коменти з ДОУ вголос читаєте?

я в голос хихикаю, а ты нет?

Мені подобається, я використовую. Суржик це більше запозичення з російської, яке до того ж вживається у своєму прямому значенні. Але в російській мові немає слова кшталт. Найближче слово що дає witkionary це польське kształt, але вони трохи відрізняються тлумаченням, тому скоріше за усе це не суржик.

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

Завзятого троянці кшталту
Не струсять нічийого ґвалту
І носа хоть кому утруть;

Іван Котляревський, Енеїда, 1799 рік

Русня, будь ласка, перестаньте розказувати українцям, як нам розмовляти. У вас є своя мова, у ній і порядкуйте.

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

так можно и до нашей эры слова из украинского языка использовать

Копанєв перелогіньтесь

Vadim Kopanev Architect 11 березня 16:47
там может тогда уже вернемся на древнерусский язык? или вовсе на наскальную живопись?

dou.ua/...​rums/topic/42474/#2587109

Написать, может быть, и можно, но зачем? Тут реально www.youtube.com/watch?v=S770Fb2Jsxk

Наоборот, просто, ты пишешь простой код, а под капотом backtracking-ом. Более практический пример использования списка как монады это монадический парсер, поэтому Haskell вариант #1 для парсинга какого-нить текстового формата.

принципова різниця така ж сама як між uint і полями Галуа.

Есть F# для чудовищно узкого круга задач с параллелизмом и асинхронностью. Таких задач на галере не найдешь за всю карьеру

Насправді F# технологія для дуже широкого кола завдань. Підтримка асинхронності в c# краща ніж в f#(останній постійно наздоганяє в тому що стосується bcl), хоча раніше f# пропунував свій погляд на асинхронне програмування і був більш продвинутим за с#, але тільки в речах специфічних для f#). Для паралелізму не має різниці насправді на чому писати. Залежить від обраного підходу організації коду в проекті.

Сижу на галері — пишу на F# в энтерпрайзі уже декілька років. задачі доволі різні , багато коду, присутня алгоритмічна або логічна складова — в принципі їх і на с# можна зробити, але type safety буде страждати сильно і декларативності не буде(основні об‘єктивні глобальні відмінності), ну і те що в останньому все організовано через функції, на противагу організації коду через взаємодію об‘єктів.

якщо не секрет, в якій області працюєте? дякую

Розробка апі для різного роду органайзерів робочого процессу аудиторів.

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

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

Йогож майкрософт вже давно поховало, чи ні ?

Ще у жовтні 2022 року Microsoft пише у себе

F# is a universal programming language for writing succinct, robust and performant code.

F# allows you to write uncluttered, self-documenting code, where your focus remains on your problem domain, rather than the details of programming.

It does this without compromising on speed and compatibility — it is open-source, cross-platform and interoperable.

learn.microsoft.com/...​net/fsharp/what-is-fsharp

Всіх би так ховали

Был бы он более С-подобный, пошел бы жваво.

кому як. а як на мене, то в ньому забагато «С-подобного»)

Підписатись на коментарі