Comments 57
Action в MVC-контроллере добавления в корзину:
public IActionResult Add(string a, string b, string c, string d, int count = 1, int mod=0)
=> _dal.GetUrl(a, b, c, d).Convert(_dal.GetProduct)
.IfNotNull(x =>
_dal.AddProductToCart(_dal.GetCartBySession(HttpContext.Session.Id), x, count, mod)
.Extend(x).Convert(PartialView), () => (IActionResult) NotFound());
Понажимайте «вверх», кого заинтересует раскрытие данной темы.
У меня уже два рабочих проекта в таком стиле сделано. Но есть два минуса такого подхода, могу позже объяснить что к чему…
У вас ведь написаны 8 вариантов функции Extend?
У вас учитывается, что в Tuple<T1,T2,T3,T4,T5,T6,T7,TRest> последний параметр должен быть тоже Tuple?
Осталось сделать рабочий компилируемый пример и описать плюсы и минусы…
Кому интересна данная тема, милости просим!
Например, можете ли вы привести универсальный способ каррировать произвольную функцию?
Или как-то ограничить функцию чтобы она не могла иметь побочных эффектов?
Я не спорю, что на C# можно писать в функциональном стиле, но называть C# функциональным языком я бы не стал.
контроля за сайд-эффектамиА это что, можете пример привести? Вы про что то вроде указать методу ключевое слово «pure» и что бы компилятор запрещал изменять состояния?
Для функционального программирования очень важно чтобы функции не имели видимых побочных эффектов. Например в Haskell все функции по умолчанию чистые. Очевидно, что в C# это не так.
И мне интересно можно ли как-то пойти обратным путём и как-то контролировать побочные эффекты функций.
Например, есть System.Diagnostics.Contracts.PureAttribute и JetBrains.Annotations.PureAttribute, но, насколько я понимаю, они не контролируют побочные эффекты метода.
Есть даже пропозал C# Pure Function Keyword to Mark No Side Effects Or External Dependencies, но пока, увы, только на рассмотрении.
Если под ленивостью понимается ленивая инициализация и исполнения, то на этом весь Linq построен.
1. SQL-подобный синтаксис, который просто транслируется в вызовы определённых методов и ничего про их ленивость не знает.
2. Возможность в C#, которая позволяет транслировать лямбда выражения в деревья выражений. Тоже никакого отношения к ленивости не имеет.
3. Набор методов расширений из пространств имён System.Linq, System.Xml.Linq и System.Data.Linq. Часть из которых ленивые, а часть из которых нет(Sum, All, Min, Max, и т.д.)
Так что я не могу согласиться с тем, что «на этом(ленивость) весь Linq построен».
Но даже если бы это было так, LINQ это только часть C#. И как раз LINQ — это кусочек функционального программирования в C#. Я слышал, что его даже хотели назвать LINM(Language Integrated Monads), но решили, что будет понятнее Queries.
Впрочем, ленивость — это, наверное, самая незначительная вещь для ФП.
var subCollection = collection.Where(o => o.someFlag = true).Select(o => o.EmbedObject).OrderBy(o => o.Title);
// some other code
var first = subCollection.FirstOrDefault() // <= вычисление Linq запроса произойдёт только здесь.
Ленивая инициализация:
var lazyLargeObject = new Lazy<LargeObject>(() =>
{
LargeObject large = new LargeObject(Thread.CurrentThread.ManagedThreadId);
// Perform additional initialization here.
return large;
});
LargeObject large = lazyLargeObject.Value // иничиализация инстанса LargeObject произойдет тут.
Впрочем, и то и то не возможности языка, но возможности библиотеки.
А насчет каррирования, вы хотите что-то вроде https://m.habrahabr.ru/post/76545/ ?
В принципе, конечно можно один раз сделать 100500 вариантов функции Curry один раз на все случаи жизни.
Более того, можно взять language-ext в котором это уже сделано(правда только для Func, хорошо бы ещё и для Action).
Но в общем, согласен, что это решает проблему с каррированием.
Скажем, Haskell язык ленивый, а Scala нет. Это делает скалу другим языком, нежели Haskell, но это никаким образом не делает скалу «не ФП» языком. Тоже самое и с побочными эффектами. Чистота функций — это очень полезное свойство, но ее отсутствие тоже не приговор.
Это ортогональные вещи.
Впрочем, если вы хотели сказать, что таких как C# «почти функциональных» языков сегодня пруд пруди — так это чистая правда.
Регистрируемые типы (неизменяемые типы без трафаретного кода)Что такое изменяемые типы? В c# в частности.
Алгебраические типы данных (мощное дополнение к системе типов)Что это вообще такое
Примеры:
data Bool = True | False-- (логическое или). Работает примерно как enum
data User = Name Age -- (логическое и). Объединяет 2 типа Name и Age в 1.
Аналоги в C#
enum Bool = True, False
class User {
Name Name;
Age Age;
}
Но вот чего нельзя сделать в C# — это комбинация этих операций:
data Maybe a = Nothing | Just a -- тип значение которого может отсутствовать.
В C# близко к этому null, но в отличие от ADT тут проверка будет не на этапе компиляции, а в рантайме.
В Haskell проверка тоже будет в рантайме
Да нет же. Не знаю как в Rust, но если вы используете Maybe значит вы явно обрабатываете случай отсутствия значения.
Возможно вы имели ввиду вот такой вариант:
myFunction:: Maybe Int -> Int
myFunction (Just a) = a
Здесь мы явно отказываемся от кейса Nothing. Но при компиляции с -W компилятор нам об этом подскажет.
Вообще как мне кажется это очень плохая практика использовать Maybe в качестве входного параметра функции, она ведь является монадой и Haskell сам сможет построить в случае необходимости композицию из нескольких Maybe.
Т.е. в предыд. примере функция должна быть вроде этой:
myFunction::Int -> Int
myFunction a = a
-- ну или просто
myFunction = id
Автор выделил 3 критерия, которым должен соответствовать язык, чтобы называться функциональным(хотя я с ними не согласен, но допустим):
- Иметь функции высшего порядка
- Неизменяемость(иммутабельность) по умолчанию
- Автоматическое управление памятью
C# не соответствует пункту №2, т.е. у него есть только функции высшего порядка и сборка мусора.
Навскидку функции высшего порядка и сборка мусора есть в D, Python, JavaScript, Java, PHP, Ruby, Swift и Go.
Сюда ещё можно отнести C++, так как хоть у него и нет сборки мусора, но есть умные указатели и функции высшего порядка.
P.S.: А почему убрали ссылку на Functional Programming in C#? В оригинальной статье она есть.
Или я что не понимаю…
Люди передали функцию в функцию и думают что это ФП. Поэтому думают что на С# можно написать функциональный код, так что f# не нужен.
В ФП есть только типы и функции. Нет переменных, нет сравнений, нет действий.
Например
let a = 5
// остальной код
На самом деле
(fun a -> //остальной код)(5)
Все функции имеют один параметр и всегда возвращают одно значение. Функции порождают функции, их можно частично применять и получать другие функции.
В ФП нет циклов, нет тупиков, вы не можете заранее выйти из функции (if (??) return;
).
На пальцах примерно так. Это совсем другой мир.
Вас не заставляют использовать не идиоматические элементы. В конце концов, в идеальном функциональном языке нет вычислений, но мы живем в реальном мире и иногда нужно писать императивно, потому что код в конце концов обрабатывается процессором.
Языковой фашизм какой-то http://stackoverflow.com/questions/993124/does-haskell-have-variables
На мой взгляд C# до функциональности далеко еще из за нехватки read-only по умолчанию и везде, судя по тек тенденции команда поддержи языка идет по противоположному пути, ака readwrite по умолчанию.
Язык C# почти функционален