직장에서는 가장 중요한 비즈니스 로직에 대한 C# 코드에서 IO를 제어하기 위해 모나드를 사용합니다.두 가지 예는 고객을위한 최적화 문제에 대한 해결책을 찾는 금융 코드와 코드입니다.
재무 코드에서 우리는 모나드를 사용하여 데이터베이스에 대한 IO 쓰기 및 읽기를 제어합니다. 기본적으로 모나드 연산을위한 작은 연산 세트와 추상 구문 트리로 구성됩니다.
interface IFinancialOperationVisitor<T, out R> : IMonadicActionVisitor<T, R> {
R GetTransactions(GetTransactions op);
R PostTransaction(PostTransaction op);
}
interface IFinancialOperation<T> {
R Accept<R>(IFinancialOperationVisitor<T, R> visitor);
}
class GetTransactions : IFinancialOperation<IError<IEnumerable<Transaction>>> {
Account Account {get; set;};
public R Accept<R>(IFinancialOperationVisitor<R> visitor) {
return visitor.Accept(this);
}
}
class PostTransaction : IFinancialOperation<IError<Unit>> {
Transaction Transaction {get; set;};
public R Accept<R>(IFinancialOperationVisitor<R> visitor) {
return visitor.Accept(this);
}
}
본질적으로 모나드에서 행동의 건설을위한 추상 구문 트리와 함께 하스켈 코드
data FinancialOperation a where
GetTransactions :: Account -> FinancialOperation (Either Error [Transaction])
PostTransaction :: Transaction -> FinancialOperation (Either Error Unit)
이며, 본질적으로 : 당신은이 (실제하지 코드) 같은 것을 상상할 수있다 무료 모나드 : 실제 코드에서
interface IMonadicActionVisitor<in T, out R> {
R Return(T value);
R Bind<TIn>(IMonadicAction<TIn> input, Func<TIn, IMonadicAction<T>> projection);
R Fail(Errors errors);
}
// Objects to remember the arguments, and pass them to the visitor, just like above
// Hopefully I got the variance right on everything for doing this without higher order types, which is how we used to do this. We now use higher order types in c#, more on that below. Here, to avoid a higher-order type, the AST for monadic actions is included by inheritance in
, 그래서 우리는 뭔가 .Select()
대신에 의해 만들어진 것으로 기억이 더있다 효율성을 위해. 중간 계산을 포함한 재무 운영은 여전히 IFinancialOperation<T>
유형입니다. 작업의 실제 성능은 통역사에 의해 수행됩니다. 통역사는 모든 데이터베이스 작업을 트랜잭션으로 래핑하고 구성 요소가 실패 할 경우 해당 트랜잭션을 롤백하는 방법을 처리합니다. 우리는 또한 단위 테스트를 위해 통역사를 사용합니다.
최적화 코드에서 우리는 최적화를 위해 외부 데이터를 얻기 위해 IO를 제어하기 위해 모나드를 사용합니다. 이것은 우리가 우리가 여러 설정에서 정확히 같은 비즈니스 코드를 사용할 수 있습니다 계산을 구성하는 방법의 무지 코드를 작성할 수 있습니다 :
- 동기 IO 및
- 비동기 IO 요구에 따라 수행 계산과 계산에 대한 계산 코드가 사용할 모나드 통과해야하기 때문에 단위 병렬
- 조롱 IO에서 수행 많은 계산에
을 테스트, 우리는 모나드의 명시 적 정의가 필요합니다. 여기에 하나 있습니다. IEncapsulated<TClass,T>
은 본질적으로 TClass<T>
을 의미합니다. 이를 통해 C# 컴파일러는 모나드 유형을 세 가지 모두 동시에 추적하여 모나드 자체를 처리 할 때 캐스팅해야 할 필요성을 극복 할 수 있습니다.
public interface IMonadGetSomething<M> : IMonadFail<Error> {
IEncapsulated<M, Something> GetSomething();
}
그런 다음 우리가 계산하는 방법에 대해 알고하지 않는 코드를 작성할 수 있습니다 :
public interface IEncapsulated<TClass,out T>
{
TClass Class { get; }
}
public interface IFunctor<F> where F : IFunctor<F>
{
// Map
IEncapsulated<F, B> Select<A, B>(IEncapsulated<F, A> initial, Func<A, B> projection);
}
public interface IApplicativeFunctor<F> : IFunctor<F> where F : IApplicativeFunctor<F>
{
// Return/Pure
IEncapsulated<F, A> Return<A>(A value);
IEncapsulated<F, B> Apply<A, B>(IEncapsulated<F, Func<A, B>> projection, IEncapsulated<F, A> initial);
}
public interface IMonad<M> : IApplicativeFunctor<M> where M : IMonad<M>
{
// Bind
IEncapsulated<M, B> SelectMany<A, B>(IEncapsulated<M, A> initial, Func<A, IEncapsulated<M, B>> binding);
// Bind and project
IEncapsulated<M, C> SelectMany<A, B, C>(IEncapsulated<M, A> initial, Func<A, IEncapsulated<M, B>> binding, Func<A, B, C> projection);
}
public interface IMonadFail<M,TError> : IMonad<M> {
// Fail
IEncapsulated<M, A> Fail<A>(TError error);
}
이제 우리는 우리의 계산이 볼 수 있어야합니다 IO의 부분에 대한 모나드의 또 다른 클래스를 만드는 상상할 수 조립되어
public class Computations {
public IEncapsulated<M, IEnumerable<Something>> GetSomethings<M>(IMonadGetSomething<M> monad, int number) {
var result = monad.Return(Enumerable.Empty<Something>());
// Our developers might still like writing imperative code
for (int i = 0; i < number; i++) {
result = from existing in r1
from something in monad.GetSomething()
select r1.Concat(new []{something});
}
return result.Select(x => x.ToList());
}
}
이것은 IMonadGetSomething<>
의 동기 및 비동기 구현 모두에서 재사용 될 수있다. 이 코드에서 비동기 설정에서도 오류가 발생할 때까지 GetSomething()
이 차례로 발생합니다. (실제 생활에서 목록을 만드는 방법이 아닙니다.)
아니요. 어떤 함수도 IO를 수행하는 것이 자유롭기 때문에 자신의 코드에서 모나드 밖에서 입출력을 수행하지 않도록하고 외부 코드를 다음과 같이 표시해야하기 때문에 극도로 징계해야합니다. IO에있다. – Lee