Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#中面向铁路的编程-如何编写开关函数?_C#_F#_Functional Programming - Fatal编程技术网

C#中面向铁路的编程-如何编写开关函数?

C#中面向铁路的编程-如何编写开关函数?,c#,f#,functional-programming,C#,F#,Functional Programming,我一直在关注,并决定尝试在C#中复制它,主要是为了看看我是否可以。很抱歉这个问题太长,但是如果你熟悉ROP,那么很容易理解 他从一个受歧视的工会开始 类型结果= |"成功" |失败 …我将其转换为一个抽象的RopValue类和两个具体的实现(请注意,我已将类名更改为我更了解的类名) …与C#等价物相比,这是一个很难理解的问题!我不知道是否有一个更简单的方法来写这个,但这里是我想出的 public static RopValue<TSuccess, TFailure> Bind<

我一直在关注,并决定尝试在C#中复制它,主要是为了看看我是否可以。很抱歉这个问题太长,但是如果你熟悉ROP,那么很容易理解

他从一个受歧视的工会开始

类型结果=
|"成功"
|失败
…我将其转换为一个抽象的RopValue类和两个具体的实现(请注意,我已将类名更改为我更了解的类名)

…与C#等价物相比,这是一个很难理解的问题!我不知道是否有一个更简单的方法来写这个,但这里是我想出的

public static RopValue<TSuccess, TFailure> Bind<TSuccess, TFailure>(this RopValue<TSuccess, TFailure> input, Func<RopValue<TSuccess, TFailure>, RopValue<TSuccess, TFailure>> switchFunction) {
  if (input is Success<TSuccess, TFailure>) {
    return switchFunction(input);
  }
  return input;
}
…并编写了我的第一个验证函数

public static RopValue<Person, string> CheckName(RopValue<Person, string> res) {
  if (res.IsSuccess()) {
    Person person = ((Success<Person, string>)res).Value;
    if (string.IsNullOrWhiteSpace(person.Name)) {
      return new Failure<Person, string>("No name");
    }
    return res;
  }
  return res;
}
这要简单得多,但我无法解决如何在C#中实现这一点

另一个问题是,我们成功启动验证链,因此第一个验证函数将始终从“if”块返回某些内容,如果验证失败,则返回失败;如果通过检查,则返回成功。如果一个函数返回失败,那么验证链中的下一个函数永远不会被调用,因此我们知道这些方法永远不会被传递失败。因此,这些函数的最后一行永远无法到达(除了在一个奇怪的情况下,您手动创建了一个失败并在开始时传递了它,但这将是毫无意义的)

然后,他创建了一个开关操作符(>=>)来连接验证函数。我试过这么做,但没能成功。为了链接对函数的连续调用,看起来我必须在Func上有一个扩展方法,我认为你做不到。我到现在为止

public static RopValue<TSuccess, TFailure> Switch<TSuccess, TFailure>(this Func<TSuccess, RopValue<TSuccess, TFailure>> switch1, Func<TSuccess, RopValue<TSuccess, TFailure>> switch2, TSuccess input) {
  RopValue<TSuccess, TFailure> res1 = switch1(input);
  if (res1.IsSuccess()) {
    return switch2(((Success<TSuccess, TFailure>)res1).Value);
  }
  return new Failure<TSuccess, TFailure>(((Failure<TSuccess, TFailure>)res1).Value);
}
公共静态ROP值开关(此Func开关1、Func开关2、TSACCESS输入){
ROP1值res1=开关1(输入);
if(res1.issucess()){
返回开关2(((成功)res1).Value);
}
返回新故障(((故障)res1).Value);
}
…但不知道如何使用它

那么,有谁能解释一下我将如何编写Bind函数,以便它可以接受一个人并返回一个RopValue(就像他的那样)?另外,我如何编写一个开关函数来连接简单的验证函数


欢迎对我的代码提出任何其他意见。我不确定它是否尽可能简洁明了。

您的
Bind
函数的类型错误,应该是:

public static RopValue<TOut, TFailure> Bind<TSuccess, TFailure, TOut>(this RopValue<TSuccess, TFailure> input, Func<TSuccess, RopValue<TOut, TFailure>> switchFunction) {
  if (input is Success<TSuccess, TFailure>) {
    return switchFunction(((Success<TSuccess, TFailure>)input).Value);
  }
  return new Failure<TOut, TFailure>(((Failure<TSuccess, TFailure>)input).Value);
}
然后可以避免在链的开始处创建虚拟值:

private static RopValue<Person, string> Validate(Person person) {
  return CheckName(person)
    .Bind(CheckEmail)
    .Bind(CheckAge);
}
私有静态ROP值验证(个人){
返回支票姓名(人)
.Bind(检查电子邮件)
.装订(支票);
}

Lee认为绑定函数定义不正确是正确的

Bind应该始终具有如下类型的签名:
m

我这样定义它,但Lee的功能相同:

public static RopValue<TSuccess2, TFailure> Bind<TSuccess, TSuccess2, TFailure>(this RopValue<TSuccess, TFailure> input, Func<TSuccess, RopValue<TSuccess2, TFailure>> switchFunction)
{
    if (input.IsSuccess)
    {
        return switchFunction(((Success<TSuccess,TFailure>)input).Value);
    }
    return new Failure<TSuccess2, TFailure>(((Failure<TSuccess, TFailure>)input).Value);
}
您可以在
Func
上定义扩展方法,但诀窍是让编译器看到这些扩展方法是可用的,类似这样的方法可以工作:

Func<Entry, RopValue<Request, string>> checkEmail = CheckEmail;
var combined = checkEmail.Kleisli(CheckAge);
RopValue<Request, string> result = combined(request);
Func checkEmail=checkEmail;
var combined=checkEmail.Kleisli(CheckAge);
RopValue结果=组合(请求);
其中,
请求
是要验证的数据


请注意,通过创建
Func
类型的变量,它允许我们使用扩展方法。

这一切让我觉得与
错误
monad类似。你是否觉得这是因为
t故障
类型实际上相当于
异常
?@Enigmativity引用马克·吐温的话,“我很高兴能够快速回答,我说我不知道!”-我是一名C程序员,同时学习F和函数编程。我已经看到讨论过单子,但还没有弄清楚它们是什么。您的
RopValue
类型是单子。基本上,monad赋予普通类型超能力。@avrohmyisroel您可以将monad视为函数式程序员使用的一种设计模式。您已经熟悉的一个简单示例是List monad,LINQ基本上是它的一个实现。这里的
bind
函数被称为
SelectMany
,如果您查看该函数的类型签名,您将看到它与我在回答中显示的表单相匹配。Monads还可以让你做一些更有趣的事情,比如创建一种引用透明的跟踪状态的方式。@Enigmativity“基本上Monads赋予普通类型超能力”-有点像编码世界的X-Kryptonite,嗯?我的答案允许更改成功类型,这就是
TOut
参数的作用。@Lee你说得对,我很抱歉,我显然要发疯了。我现在有机会看看这个。当你说我的Bind函数有错误的类型时,就我所见,唯一真正的区别是你纠正了我潜意识中假设输入和输出类型相同的简化。我不知道为什么这会改变你在我的案例中使用它的方式,因为我一直在使用Person类。您能再解释一下吗?另外,我尝试将Bind函数移动到基类,这很成功,因为我刚刚返回了switchFunction(Value),但我无法确定在Failure类中应该执行什么操作。我的想法是返回“this”(作为Failure对象本身,当然也是一个RopValue对象),但这会导致编译器错误,无法将失败转换为RopValue。选角也没用。我错过了什么?再次感谢
public static RopValue<TOut, TFailure> Bind<TSuccess, TFailure, TOut>(this RopValue<TSuccess, TFailure> input, Func<TSuccess, RopValue<TOut, TFailure>> switchFunction) {
  if (input is Success<TSuccess, TFailure>) {
    return switchFunction(((Success<TSuccess, TFailure>)input).Value);
  }
  return new Failure<TOut, TFailure>(((Failure<TSuccess, TFailure>)input).Value);
}
public abstract RopValue<TOut, TFailure> Bind<TOut>(Func<TSuccess, RopValue<TOut, TFailure> f);

public class Success<TSuccess, TFailure> : RopValue<TSuccess, TFailure> {
    public override RopValue<TOut, TFailure> Bind<TOut>(Func<TSuccess, RopValue<TOut, TFailure> f) {
        return f(this.Value);
    }
}

public class Failure<TSuccess, TFailure> : RopValue<TSuccess, TFailure> {
    public override RopValue<TOut, TFailure> Bind<TOut>(Func<TSuccess, RopValue<TOut, TFailure> f) {
        return new Failure<TOut, TFailure>(this.Value);
    }
}
private static RopValue<Person, string> Validate(Person person) {
  return CheckName(person)
    .Bind(CheckEmail)
    .Bind(CheckAge);
}
public static RopValue<TSuccess2, TFailure> Bind<TSuccess, TSuccess2, TFailure>(this RopValue<TSuccess, TFailure> input, Func<TSuccess, RopValue<TSuccess2, TFailure>> switchFunction)
{
    if (input.IsSuccess)
    {
        return switchFunction(((Success<TSuccess,TFailure>)input).Value);
    }
    return new Failure<TSuccess2, TFailure>(((Failure<TSuccess, TFailure>)input).Value);
}
public static Func<TSuccess, RopValue<TSuccess2, TFailure>> Kleisli<TSuccess, TSuccess2, TFailure>(this Func<TSuccess, RopValue<TSuccess, TFailure>> switch1, Func<TSuccess, RopValue<TSuccess2, TFailure>> switch2)
{
    return (inp => switch1(inp).Bind(switch2));
}
Func<Entry, RopValue<Request, string>> checkEmail = CheckEmail;
var combined = checkEmail.Kleisli(CheckAge);
RopValue<Request, string> result = combined(request);