Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
При образовании нового типа M<A> возникают проблемы с использованием старых функций 1) A -> B 2) (A,B) -> C Так же у нас получается новый тип функций A -> M<B> у которых есть проблемы с 3)Применением 4)Композицией между собой 5)Композицией со старыми функциями
int discount = DiscountService.GetDiscount(userId.Value, productId.Value)DiscountService.GetDiscount(userId.Value, productId.Value)Func<int,int,int> f = DiscountService.GetDiscount;
var discount = f.Curry().Pure().Apply(userId).Apply(productId);
null используется вроде аналога F#‘ного Nothing и делать проверки с этим допущением. null – в этом смысле подойдет мой метод. А каррирование например? Каррирование и функциональная композиция на практике нужны мало где – например редко где нужно делать вызовы вроде f(g(h(x))) а даже если нужно, это не так страшно. Я много пишу на F# но даже там операторы << и >> у меня не возтребованы, хотя конечно |> я пользуюсь т.к. удобно (но не критично).Maybe<T> и остались старые функции А->BuserId = UserService.GetUserId( email ) # Может быть None
productId = ProductService.GetProductId( productCode ) # Может быть None
discount = DiscountService.GetDiscount( userId, productId) # Не нужно ".value", переживает None.
if discount is None :
# Тут бизнес логика "не найдено".
int discount = DiscountService.GetDiscount(userId.Value, productId.Value) // не корректное решение
С динамическими тут вообще сложно сравнивать, так как там можно делать все что пожелаешь.
Но представьте если бы у вас DiscountService.GetDiscount на входе требовал что бы оба параметра были не None.
if userId is not None and productId is not None :
discount = DiscountService.GetDiscount( userId, productId)
Maybe<int> discount = DiscountService.GetDiscount.Curry().Pure().Apply(userId).Apply(productId);
(userId |@| productId) {DiscountService.GetDiscount(_, _)}
for {
u <- userId
p <- productId
} yield DiscountService.GetDiscount(u, p)
for {
u <- userId
p <- productId
} yield DiscountService.GetDiscount(u, p)
M<M<T>> -> M<T>List<T>. В C# наличие инструментов для других сущностный особой пользы не принесет, так как они будут не связаны, как, опять же, я написал в заключении. Не увидел смысла упоминать join, помоем достаточно bind.public Person GetFathersMothersSister(Person person)
{
if (person == null)
return null;
var father = person.GetFather();
if (father == null)
return null;
var fathersMother = father.GetMother();
if (fathersMother == null)
return null;
return fathersMother.GetSisters().FirstOrDefault();
}
public Maybe<Person> GetFathersMothersSister(Maybe<Person> person)
{
return
person
.FlatMap(p => p.GetFather())
.FlatMap(f => f.GetMother())
.FlatMap(m => m.GetSisters().FirstMaybe());
}
f = length . filter (>3) . map (+1) . skip 3 (f ∘ g)(x) = f(g(x))(length ∘ (filter (>3)) ∘ (map (+1)) ∘ (skip 3)) x = length((filter (>3))((map (+1))((skip 3)(x))))Seq.length << (Seq.filter ((>) 3)) << (Seq.map ((+) 1)) << (Seq.skip 3)(Seq.skip 3) >> (Seq.map ((+) 1)) >> (Seq.filter ((>) 3)) >> Seq.lengthskip 3 |> map (+1) |> filter (>3) |> lengthsomeList |> Seq.skip 3 |> Seq.map ((+) 1) |> Seq.filter ((>) 3) |> Seq.length
> (|>);;
val it : ('a -> ('a -> 'b) -> 'b)
> (>>);;
val it : (('a -> 'b) -> ('b -> 'c) -> 'a -> 'c)
Только в примере мы получаем дизайн программы, и знаем что она будет работать.
Prelude> take 0 undefined
[]
Prelude> take 0 $! undefined
*** Exception: Prelude.undefined
Prelude> take 0 undefined []
Prelude> take 0 $! undefined
*** Exception: Prelude.undefined
Если программа проходит согласование по типам, она работоспособна при правильной реализации.
А если компилятор пропустил деление на ноль, и об этом узнали после релиза, я склонен считать что с инструментом есть некие проблемы.
Для тех кто не знаком с C#, ключевое слово this позволяет использовать функцию через точку после объекта
Maybe discount = f.Curry().Pure().Apply(userId).Apply(productId);
Извините, но ничего декларативного в этом коде нет. Он вообще не показывает бизнес-намерение кода.
получить запрос >>=
проверить корректность >>=
проверить url >>=
проверить пользователя >>=
выбрать результат из базы >>=
нарисовать и отправить ответ
если на каком нибудь этапе, обработка провалится, результат будет Nothing или кому более привычный null
Exception в многопоточном коде не всегда удобен.
позволяет вместо броска исключения вернуть результат неудачной проверки, например
Но логику работы программы они ломают
И на исклбючениях строить бизнеслогику — зло.
Возвращать результат проверки тем же методом, что и корректный результат осмысленно хотя бы потому, что уменььшается количество точек выхода. Да и логика становится более очевидной.
Nothing это и есть exception для чистых вычислений (для данного примера).
data Either a b = Left a | Right b
если мы используем исключения для обработки ожидаемых ошибок (неавторизованность пользователя — это ведь ожидаемая ошибка?)
а действительно ли он в случае ожидаемой ошибки кинет исключение нужного типа?
Внутри метода «получи данные»? Нет, не ожидаемая. Мы предполагаем, что пользователь пришел с правильным контекстом.
А альтернатива какая? Получать null и пытаться угадать, там нет данных, или у нас не авторизовался пользователь?
В таком случае нам не нужно в конце получать null и продолжать работу — нам нужно падать с логом в тот момент, как оказалось что пользователь пришел с неправильным контекстом.
Альтернатива чему?
вебсервис = нарисовать и отправить ответ . выбрать результат из базы . проверить пользователя . проверить url . проверить корректность
Легкая прогулка от функтора через монаду к стрелке