Language-Ext,如何在 C# 中使用应用程序?
Language-Ext, How to use applicatives in c#?
您好,我正在构建一个来自 https://fsharpforfunandprofit.com/posts/elevated-world-3/ 的 f# 示例
在 c# 中。
我的代码如下,
public class CustomerId : NewType<CustomerId, int> { public CustomerId(int id) : base(id) { } }
public class EmailAddress : NewType<EmailAddress, string> { public EmailAddress(string email) : base(email) { } }
public class Customer : Record<Customer>
{
public readonly CustomerId Id;
public readonly EmailAddress Email;
public Customer(CustomerId id, EmailAddress email)
{
Id = id;
Email = email;
}
}
public static class CustomerConstructor
{
public static Result<CustomerId> CreateCustomerId(int id)
{
if (id > 0) return new Result<CustomerId>.Success(new CustomerId(id));
else return new Result<CustomerId>.Error(new[] { "invalid id" });
}
public static Result<EmailAddress> CreateCustomerEmail(string email)
{
if (string.IsNullOrEmpty(email)) return new Result<EmailAddress>.Error(new[] { "empty email" });
else if (!email.Contains("@")) return new Result<EmailAddress>.Error(new[] { "invalid email" });
else return new Result<EmailAddress>.Success(new EmailAddress(email));
}
}
public abstract class Result<A>
{
public class Success : Result<A>
{
public readonly A Value;
public Success(A value)
{
Value = value;
}
}
public class Error : Result<A>
{
public readonly Arr<string> Errors;
public Error(IEnumerable<string> errors)
{
Errors = errors.ToArr();
}
}
}
public static class ResultModule
{
public static UnitTest1.Result<A> Return<A>(this UnitTest1.Result<A> self, A a)
{
return new UnitTest1.Result<A>.Success(a);
}
public static UnitTest1.Result<A> Return<A>(A a)
{
return new UnitTest1.Result<A>.Success(a);
}
public static UnitTest1.Result<B> Select<A, B>(this UnitTest1.Result<A> self, Func<A, B> map)
=> Map<A, B>(self, map);
public static UnitTest1.Result<B> Map<A, B>(this UnitTest1.Result<A> self, Func<A, B> map)
{
if (self is UnitTest1.Result<A>.Success)
{
var sx = (UnitTest1.Result<A>.Success)self;
return new UnitTest1.Result<B>.Success(map(sx.Value));
}
else
{
var er = (UnitTest1.Result<A>.Error)self;
return new UnitTest1.Result<B>.Error(er.Errors);
}
}
public static UnitTest1.Result<B> ApplyMine<A, B>(this UnitTest1.Result<A> self, UnitTest1.Result<Func<A, B>> apply)
{
if (apply is UnitTest1.Result<Func<A, B>>.Success && self is UnitTest1.Result<A>.Success)
{
var f = (UnitTest1.Result<Func<A, B>>.Success)apply;
var x = (UnitTest1.Result<A>.Success)self;
return new UnitTest1.Result<B>.Success(f.Value(x.Value));
}
if (apply is UnitTest1.Result<Func<A, B>>.Error && self is UnitTest1.Result<A>.Success)
{
var f = (UnitTest1.Result<Func<A, B>>.Error)apply;
return new UnitTest1.Result<B>.Error(f.Errors);
}
if (apply is UnitTest1.Result<Func<A, B>>.Success && self is UnitTest1.Result<A>.Error)
{
var x = (UnitTest1.Result<A>.Error)self;
return new UnitTest1.Result<B>.Error(x.Errors);
}
if (apply is UnitTest1.Result<Func<A, B>>.Error && self is UnitTest1.Result<A>.Error)
{
var f = (UnitTest1.Result<Func<A, B>>.Error)apply;
var x = (UnitTest1.Result<A>.Error)self;
return new UnitTest1.Result<B>.Error(f.Errors.Concat(x.Errors));
}
return default(UnitTest1.Result<B>);//fn should never hit here
}
public static UnitTest1.Result<B> Bind<A, B>(this UnitTest1.Result<A> self, Func<A, UnitTest1.Result<B>> bind)
{
if (self is UnitTest1.Result<A>.Success)
{
var sx = (UnitTest1.Result<A>.Success)self;
return bind(sx.Value);
}
else
{
var er = (UnitTest1.Result<A>.Error)self;
return new UnitTest1.Result<B>.Error(er.Errors);
}
}
public static UnitTest1.Result<C> SelectMany<A, B, C>(this UnitTest1.Result<A> self, Func<A, UnitTest1.Result<B>> bind, Func<A, B, C> project)
{
var bound = Bind<A, B>(self, bind);
if (bound is UnitTest1.Result<B>.Success)
{
var sxA = (UnitTest1.Result<A>.Success)self;
var sxB = (UnitTest1.Result<B>.Success)bound;
return new UnitTest1.Result<C>.Success(project(sxA.Value, sxB.Value));
}
else
{
var er = (UnitTest1.Result<A>.Error)self;
return new UnitTest1.Result<C>.Error(er.Errors);
}
}
}
注:UnitTest1是添加的命名空间(因为LanguageExt中有Result类型)
上面我测试的代码如下
[TestMethod]
public void TestApplicativeValidation()
{
var goodId = 1;
var badId = 0;
var goodEmail = "test@example.com";
var badEmail = "example.com";
Func<CustomerId, EmailAddress, Customer> createCustomer = (id, email) => new Customer(id, email);
var idResult = CustomerConstructor.CreateCustomerId(goodId);
var emailResult = CustomerConstructor.CreateCustomerEmail(goodEmail);
var createCustomer1 = ResultModule.Return(createCustomer);
//ResultModule.ApplyMine(idResult, )
}
[TestMethod]
public void TestMonadaicValidation()
{
var goodId = 1;
var badId = 0;
var goodEmail = "test@example.com";
var badEmail = "example.com";
var goodCust = from id in CustomerConstructor.CreateCustomerId(goodId)
from email in CustomerConstructor.CreateCustomerEmail(goodEmail)
select new Customer(id, email);
var badCust = from id in CustomerConstructor.CreateCustomerId(badId)
from email in CustomerConstructor.CreateCustomerEmail(badEmail)
select new Customer(id, email);
}
Monadiac 测试按预期运行并且全部找到,但我无法编写测试来检查 link、
中的应用场景
let (<!>) = Result.map
let (<*>) = Result.apply
// applicative version
let createCustomerResultA id email =
let idResult = createCustomerId id
let emailResult = createEmailAddress email
createCustomer <!> idResult <*> emailResult
// int -> string -> Result<CustomerInfo>
任何人都可以指导我在这里提出一些见解,我们有一个 linq 表达式自动使用 select / select 许多,在应用风格的情况下呢?
您正在寻找 Validation
类型的 language-ext 来实现该示例。我不会为您完成所有工作,但您可以查看 one of the units tests,其中有一个使用 Validation
类型的应用行为的真实示例。
language-ext 中的大多数核心类型都支持应用行为through the apply
function。
我已经找到上面 ApplyMine 函数的正确实现。下面是applicative的测试用例。
[TestMethod]
public void TestApplicativeValidation()
{
var goodId = 1;
var badId = 0;
var goodEmail = "test@example.com";
var badEmail = "example.com";
Func<CustomerId, EmailAddress, Customer> createCustomer = (id, email) => new Customer(id, email);
/*
var idResult = CustomerConstructor.CreateCustomerId(goodId);
var emailResult = CustomerConstructor.CreateCustomerEmail(goodEmail);
var goodCustomer = idResult.Lift2(emailResult, createCustomer);
*/
var good = CustomerConstructor.CreateCustomerId(goodId).Lift2(CustomerConstructor.CreateCustomerEmail(goodEmail), createCustomer);
var bad22 = CustomerConstructor.CreateCustomerId(badId).Lift2(CustomerConstructor.CreateCustomerEmail(badEmail), createCustomer);
var bad1 = CustomerConstructor.CreateCustomerId(goodId).Lift2(CustomerConstructor.CreateCustomerEmail(badEmail), createCustomer);
var bad2 = CustomerConstructor.CreateCustomerId(badId).Lift2(CustomerConstructor.CreateCustomerEmail(goodEmail), createCustomer);
}
这是添加到扩展 class / 模块的 Lift2 实现。
public static UnitTest1.Result<C> Lift2<A, B, C>(this UnitTest1.Result<A> self, UnitTest1.Result<B> other, Func<A, B, C> lift2)
{
Func<A, Func<B, C>> lifter = a => b => lift2(a, b);
var aBakedIn = self.ApplyMine(ResultModule.Return(lifter));
return other.ApplyMine(aBakedIn);
}
在 csharp 中没有用于应用程序风格编程的表达式,而对于 monadiac 风格我们有 linq
var goodCust = from id in CustomerConstructor.CreateCustomerId(goodId)
from email in CustomerConstructor.CreateCustomerEmail(goodEmail)
select new Customer(id, email);
,用2个monad和2个参数的函数显式调用lift2会更简洁。就像 language-ext 通过 Prelute 所做的那样。我决定在使用函数式结构时也遵循 "dot into notation" in c sharp。
当我脑袋放屁的时候,这篇文章来拯救我!
http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
您好,我正在构建一个来自 https://fsharpforfunandprofit.com/posts/elevated-world-3/ 的 f# 示例 在 c# 中。
我的代码如下,
public class CustomerId : NewType<CustomerId, int> { public CustomerId(int id) : base(id) { } }
public class EmailAddress : NewType<EmailAddress, string> { public EmailAddress(string email) : base(email) { } }
public class Customer : Record<Customer>
{
public readonly CustomerId Id;
public readonly EmailAddress Email;
public Customer(CustomerId id, EmailAddress email)
{
Id = id;
Email = email;
}
}
public static class CustomerConstructor
{
public static Result<CustomerId> CreateCustomerId(int id)
{
if (id > 0) return new Result<CustomerId>.Success(new CustomerId(id));
else return new Result<CustomerId>.Error(new[] { "invalid id" });
}
public static Result<EmailAddress> CreateCustomerEmail(string email)
{
if (string.IsNullOrEmpty(email)) return new Result<EmailAddress>.Error(new[] { "empty email" });
else if (!email.Contains("@")) return new Result<EmailAddress>.Error(new[] { "invalid email" });
else return new Result<EmailAddress>.Success(new EmailAddress(email));
}
}
public abstract class Result<A>
{
public class Success : Result<A>
{
public readonly A Value;
public Success(A value)
{
Value = value;
}
}
public class Error : Result<A>
{
public readonly Arr<string> Errors;
public Error(IEnumerable<string> errors)
{
Errors = errors.ToArr();
}
}
}
public static class ResultModule
{
public static UnitTest1.Result<A> Return<A>(this UnitTest1.Result<A> self, A a)
{
return new UnitTest1.Result<A>.Success(a);
}
public static UnitTest1.Result<A> Return<A>(A a)
{
return new UnitTest1.Result<A>.Success(a);
}
public static UnitTest1.Result<B> Select<A, B>(this UnitTest1.Result<A> self, Func<A, B> map)
=> Map<A, B>(self, map);
public static UnitTest1.Result<B> Map<A, B>(this UnitTest1.Result<A> self, Func<A, B> map)
{
if (self is UnitTest1.Result<A>.Success)
{
var sx = (UnitTest1.Result<A>.Success)self;
return new UnitTest1.Result<B>.Success(map(sx.Value));
}
else
{
var er = (UnitTest1.Result<A>.Error)self;
return new UnitTest1.Result<B>.Error(er.Errors);
}
}
public static UnitTest1.Result<B> ApplyMine<A, B>(this UnitTest1.Result<A> self, UnitTest1.Result<Func<A, B>> apply)
{
if (apply is UnitTest1.Result<Func<A, B>>.Success && self is UnitTest1.Result<A>.Success)
{
var f = (UnitTest1.Result<Func<A, B>>.Success)apply;
var x = (UnitTest1.Result<A>.Success)self;
return new UnitTest1.Result<B>.Success(f.Value(x.Value));
}
if (apply is UnitTest1.Result<Func<A, B>>.Error && self is UnitTest1.Result<A>.Success)
{
var f = (UnitTest1.Result<Func<A, B>>.Error)apply;
return new UnitTest1.Result<B>.Error(f.Errors);
}
if (apply is UnitTest1.Result<Func<A, B>>.Success && self is UnitTest1.Result<A>.Error)
{
var x = (UnitTest1.Result<A>.Error)self;
return new UnitTest1.Result<B>.Error(x.Errors);
}
if (apply is UnitTest1.Result<Func<A, B>>.Error && self is UnitTest1.Result<A>.Error)
{
var f = (UnitTest1.Result<Func<A, B>>.Error)apply;
var x = (UnitTest1.Result<A>.Error)self;
return new UnitTest1.Result<B>.Error(f.Errors.Concat(x.Errors));
}
return default(UnitTest1.Result<B>);//fn should never hit here
}
public static UnitTest1.Result<B> Bind<A, B>(this UnitTest1.Result<A> self, Func<A, UnitTest1.Result<B>> bind)
{
if (self is UnitTest1.Result<A>.Success)
{
var sx = (UnitTest1.Result<A>.Success)self;
return bind(sx.Value);
}
else
{
var er = (UnitTest1.Result<A>.Error)self;
return new UnitTest1.Result<B>.Error(er.Errors);
}
}
public static UnitTest1.Result<C> SelectMany<A, B, C>(this UnitTest1.Result<A> self, Func<A, UnitTest1.Result<B>> bind, Func<A, B, C> project)
{
var bound = Bind<A, B>(self, bind);
if (bound is UnitTest1.Result<B>.Success)
{
var sxA = (UnitTest1.Result<A>.Success)self;
var sxB = (UnitTest1.Result<B>.Success)bound;
return new UnitTest1.Result<C>.Success(project(sxA.Value, sxB.Value));
}
else
{
var er = (UnitTest1.Result<A>.Error)self;
return new UnitTest1.Result<C>.Error(er.Errors);
}
}
}
注:UnitTest1是添加的命名空间(因为LanguageExt中有Result类型)
上面我测试的代码如下
[TestMethod]
public void TestApplicativeValidation()
{
var goodId = 1;
var badId = 0;
var goodEmail = "test@example.com";
var badEmail = "example.com";
Func<CustomerId, EmailAddress, Customer> createCustomer = (id, email) => new Customer(id, email);
var idResult = CustomerConstructor.CreateCustomerId(goodId);
var emailResult = CustomerConstructor.CreateCustomerEmail(goodEmail);
var createCustomer1 = ResultModule.Return(createCustomer);
//ResultModule.ApplyMine(idResult, )
}
[TestMethod]
public void TestMonadaicValidation()
{
var goodId = 1;
var badId = 0;
var goodEmail = "test@example.com";
var badEmail = "example.com";
var goodCust = from id in CustomerConstructor.CreateCustomerId(goodId)
from email in CustomerConstructor.CreateCustomerEmail(goodEmail)
select new Customer(id, email);
var badCust = from id in CustomerConstructor.CreateCustomerId(badId)
from email in CustomerConstructor.CreateCustomerEmail(badEmail)
select new Customer(id, email);
}
Monadiac 测试按预期运行并且全部找到,但我无法编写测试来检查 link、
中的应用场景let (<!>) = Result.map
let (<*>) = Result.apply
// applicative version
let createCustomerResultA id email =
let idResult = createCustomerId id
let emailResult = createEmailAddress email
createCustomer <!> idResult <*> emailResult
// int -> string -> Result<CustomerInfo>
任何人都可以指导我在这里提出一些见解,我们有一个 linq 表达式自动使用 select / select 许多,在应用风格的情况下呢?
您正在寻找 Validation
类型的 language-ext 来实现该示例。我不会为您完成所有工作,但您可以查看 one of the units tests,其中有一个使用 Validation
类型的应用行为的真实示例。
language-ext 中的大多数核心类型都支持应用行为through the apply
function。
我已经找到上面 ApplyMine 函数的正确实现。下面是applicative的测试用例。
[TestMethod]
public void TestApplicativeValidation()
{
var goodId = 1;
var badId = 0;
var goodEmail = "test@example.com";
var badEmail = "example.com";
Func<CustomerId, EmailAddress, Customer> createCustomer = (id, email) => new Customer(id, email);
/*
var idResult = CustomerConstructor.CreateCustomerId(goodId);
var emailResult = CustomerConstructor.CreateCustomerEmail(goodEmail);
var goodCustomer = idResult.Lift2(emailResult, createCustomer);
*/
var good = CustomerConstructor.CreateCustomerId(goodId).Lift2(CustomerConstructor.CreateCustomerEmail(goodEmail), createCustomer);
var bad22 = CustomerConstructor.CreateCustomerId(badId).Lift2(CustomerConstructor.CreateCustomerEmail(badEmail), createCustomer);
var bad1 = CustomerConstructor.CreateCustomerId(goodId).Lift2(CustomerConstructor.CreateCustomerEmail(badEmail), createCustomer);
var bad2 = CustomerConstructor.CreateCustomerId(badId).Lift2(CustomerConstructor.CreateCustomerEmail(goodEmail), createCustomer);
}
这是添加到扩展 class / 模块的 Lift2 实现。
public static UnitTest1.Result<C> Lift2<A, B, C>(this UnitTest1.Result<A> self, UnitTest1.Result<B> other, Func<A, B, C> lift2)
{
Func<A, Func<B, C>> lifter = a => b => lift2(a, b);
var aBakedIn = self.ApplyMine(ResultModule.Return(lifter));
return other.ApplyMine(aBakedIn);
}
在 csharp 中没有用于应用程序风格编程的表达式,而对于 monadiac 风格我们有 linq
var goodCust = from id in CustomerConstructor.CreateCustomerId(goodId)
from email in CustomerConstructor.CreateCustomerEmail(goodEmail)
select new Customer(id, email);
,用2个monad和2个参数的函数显式调用lift2会更简洁。就像 language-ext 通过 Prelute 所做的那样。我决定在使用函数式结构时也遵循 "dot into notation" in c sharp。
当我脑袋放屁的时候,这篇文章来拯救我! http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html