Pull to refresh

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());

Понажимайте «вверх», кого заинтересует раскрытие данной темы.
У меня уже два рабочих проекта в таком стиле сделано. Но есть два минуса такого подхода, могу позже объяснить что к чему…
Extend — это функция создающая Tuple из входящего Tuple или одного типа, добавляя к нему еще один элемент. Например, есть Tuple<int, double>, тогда Extend(Tuple<int, double>, string) вернет Tuple<int, double, string>. В данном случае, моделью для вьюшки будет являться Tuple<Cart, Product>.
Вы имеете ввиду, Tuple<T1,T2,T3>? Хоть пятерку… Но это не очень хорошо, использовать такие длинные записи для логики…
и кто сказал что это фп? то что вы сделали флюент это еще не значит, где правильный карринг и все остальное? Ваши идеи можно сделать с помощью монадических цепочек + Maybe/Either. Также Rx достаточно самодостаточен и очень приближен к идеям фп. Есть еще вариант с Validation монадой, если надо накапливать причины ошибок
Я и не говорю что это ФП, даже намека не делаю, но такой стиль написания на C# имеет место быть и может называться функциональным стилем.
Да, это просто Fluent! И он прекрасен!!!
Я не совсем понимаю, что Вы хотели увидеть… Разговор ведется в рамках императивного языка программирования C#, я использую лишь другую запись, и не собираюсь делать из C# F#…
Было бы не плохо написать интерфейс для LINQ
Когда завезут Variadic Templates?
У вас ведь написаны 8 вариантов функции Extend?
У вас учитывается, что в Tuple<T1,T2,T3,T4,T5,T6,T7,TRest> последний параметр должен быть тоже Tuple?
В C# 7.0 есть записи. Нет, не учитывается, если вы имеете ввиду «склейку» Tuple. Вообще, я выше говорил, что не стоит использовать такие громоздкие записи, где будет Item7 например, это понижает читаемость, хотя дает типизацию.
Было бы интересно почитать плюсы и минусы!
Попытался описать минусы, плюсы и кое-какие советы по использование. См. readme в репозитории.
ссылка на репозиторий
Осталось сделать рабочий компилируемый пример и описать плюсы и минусы…
Кому интересна данная тема, милости просим!
И всё? Есть ли в C# возможность каррирования, частичного применения, ленивости, контроля за сайд-эффектами?
Например, можете ли вы привести универсальный способ каррировать произвольную функцию?
Или как-то ограничить функцию чтобы она не могла иметь побочных эффектов?
Я не спорю, что на C# можно писать в функциональном стиле, но называть C# функциональным языком я бы не стал.
помимо F# есть еще такой интересный проект http://elalang.net/
контроля за сайд-эффектами
А это что, можете пример привести? Вы про что то вроде указать методу ключевое слово «pure» и что бы компилятор запрещал изменять состояния?
Разверну мысль.
Для функционального программирования очень важно чтобы функции не имели видимых побочных эффектов. Например в Haskell все функции по умолчанию чистые. Очевидно, что в C# это не так.
И мне интересно можно ли как-то пойти обратным путём и как-то контролировать побочные эффекты функций.
Например, есть System.Diagnostics.Contracts.PureAttribute и JetBrains.Annotations.PureAttribute, но, насколько я понимаю, они не контролируют побочные эффекты метода.
Есть даже пропозал C# Pure Function Keyword to Mark No Side Effects Or External Dependencies, но пока, увы, только на рассмотрении.
UFO just landed and posted this here

Если под ленивостью понимается ленивая инициализация и исполнения, то на этом весь Linq построен.

Насколько я понимаю, 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 произойдет тут. 


Впрочем, и то и то не возможности языка, но возможности библиотеки.
Да, я хочу что-то вроде этого, но только чтобы не надо было добавлять новый метод-расширение, когда у меня появляется метод с другим числом параметров.
В принципе, конечно можно один раз сделать 100500 вариантов функции Curry один раз на все случаи жизни.
Более того, можно взять language-ext в котором это уже сделано(правда только для Func, хорошо бы ещё и для Action).
Но в общем, согласен, что это решает проблему с каррированием.
На самом деле, многое из того что вы написали, совершенно необязательно.

Скажем, Haskell язык ленивый, а Scala нет. Это делает скалу другим языком, нежели Haskell, но это никаким образом не делает скалу «не ФП» языком. Тоже самое и с побочными эффектами. Чистота функций — это очень полезное свойство, но ее отсутствие тоже не приговор.

Это ортогональные вещи.

Впрочем, если вы хотели сказать, что таких как C# «почти функциональных» языков сегодня пруд пруди — так это чистая правда.
Впрочем, если вы хотели сказать, что таких как C# «почти функциональных» языков сегодня пруд пруди — так это чистая правда.

И хорошо, что есть языки, позволяющие придерживаться наиболее подходящей парадигмы для конкретных задач.
Я совершенно не имел в виду, что это плохо. На мой взгляд, это как раз хорошо. Именно такие языки зачастую практически удобны.
Haskell тоже можно сделать и строгим и динамическим.
Регистрируемые типы (неизменяемые типы без трафаретного кода)
Что такое изменяемые типы? В 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 тут проверка будет не на этапе компиляции, а в рантайме.
UFO just landed and posted this here
В Haskell проверка тоже будет в рантайме

Да нет же. Не знаю как в Rust, но если вы используете Maybe значит вы явно обрабатываете случай отсутствия значения.
Возможно вы имели ввиду вот такой вариант:

myFunction:: Maybe Int -> Int
myFunction (Just a) = a

Здесь мы явно отказываемся от кейса Nothing. Но при компиляции с -W компилятор нам об этом подскажет.
Вообще как мне кажется это очень плохая практика использовать Maybe в качестве входного параметра функции, она ведь является монадой и Haskell сам сможет построить в случае необходимости композицию из нескольких Maybe.
Т.е. в предыд. примере функция должна быть вроде этой:

myFunction::Int -> Int
myFunction a = a
-- ну или просто
myFunction = id

UFO just landed and posted this here
А какой популярный язык не является «почти функциональным»?
Автор выделил 3 критерия, которым должен соответствовать язык, чтобы называться функциональным(хотя я с ними не согласен, но допустим):
  1. Иметь функции высшего порядка
  2. Неизменяемость(иммутабельность) по умолчанию
  3. Автоматическое управление памятью

C# не соответствует пункту №2, т.е. у него есть только функции высшего порядка и сборка мусора.
Навскидку функции высшего порядка и сборка мусора есть в D, Python, JavaScript, Java, PHP, Ruby, Swift и Go.
Сюда ещё можно отнести C++, так как хоть у него и нет сборки мусора, но есть умные указатели и функции высшего порядка.

P.S.: А почему убрали ссылку на Functional Programming in C#? В оригинальной статье она есть.
С функциями высшего порядка нужно быть осторожнее, а то некоторые почитают о модном функциональном подходе, и начинают возводить это в абсолют в ущерб читаемости, поддержки и тестируемости кода, в стиле «смотри как я могу!». Там где можно было обойтись логической композицией имутабельных классов (старый добрый IoC/DI) и дергать их в императивном стиле, они фигачат функции с километровыми сигнатурами которые на 27'' экран не помещаются, где функция принимает функции с кучей параметров и возвращает другую функцию с кучей параметров, некоторые из которых кортежи. Потом час сидишь и втыкаешь в эту сигнатуру и пытаешься понять что вообще происходит. Не говоря уже что дебажить это все счастье практически невозможно. Зато от поганых классов и объектов избавился, это да.

Люди передали функцию в функцию и думают что это ФП. Поэтому думают что на С# можно написать функциональный код, так что f# не нужен.

А что такое ФП? Скажите вашими словами.

В ФП есть только типы и функции. Нет переменных, нет сравнений, нет действий.


Например


    let a = 5
    // остальной код

На самом деле


    (fun a -> //остальной код)(5)

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


В ФП нет циклов, нет тупиков, вы не можете заранее выйти из функции (if (??) return;).


На пальцах примерно так. Это совсем другой мир.

Т.Е. Lisp не является функциональным языком? http://www.tutorialspoint.com/lisp/lisp_variables.htm
UFO just landed and posted this here
Ну тогда Lisp->Ocaml->F# все они не чистые.

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

Не понял вопроса. У меня проблем с F# нет.

UFO just landed and posted this here
UFO just landed and posted this here
туда пишут, от туда читают, но это не переменные…
UFO just landed and posted this here
UFO just landed and posted this here

На мой взгляд C# до функциональности далеко еще из за нехватки read-only по умолчанию и везде, судя по тек тенденции команда поддержи языка идет по противоположному пути, ака readwrite по умолчанию.

Sign up to leave a comment.