作为关于可重用库(对于我正在学习的内容)的 FP 设计的一个关键区别的前提是,这些库比相应的 OO(通常)更以数据为中心。
TFD(类型优先开发)等新兴技术似乎也证实了这一点,Tomas Petricek 在 this 中对此进行了很好的解释。博文。
如今的语言是多范式的,同一位 Petricek 在其书中解释了 C# 中可用的各种函数式技术。
我感兴趣的是如何正确划分代码。
所以我定义了库数据结构,使用等效的可区分联合(如 Petricek 书中所示),并且我计划根据我的需求的域逻辑将它们与不可变列表和/或元组一起使用 .
我在哪里放置作用于数据结构的操作(方法...函数)?
如果我想定义一个使用标准委托(delegate)中体现的函数值的高阶函数 Func<T1...TResult>
,我应该把它放在哪里?
常识告诉我应该将这些方法分组到静态类中,但我希望得到已经用 C# 编写过函数库的人的确认。
假设这是正确的并且我有一个像这样的高阶函数:
static class AnimalTopology {
IEnumerable<Animal> ListVertebrated(Func<Skeleton, bool> selector) {
// remainder omitted
}
}
如果 choosing vertebrated animal 有 N 个我想在库中公开的特定案例,那么公开它们的更正确方法是什么。
static class VertebratedSelectorsA {
// this is compatible with "Func<Skeleton, bool> selector"
static bool Algorithm1(Skeleton s) {
//...
}
}
或
static class VertebratedSelectorsB {
// this method creates the function for later application
static Func<Skeleton, bool> CreateAlgorithm1Selector(Skeleton s) {
// ...
}
}
任何指示将不胜感激。
编辑:
我想引用 T. Petricek 的两个短语,Real World Functional Programming Mads Torgersen 的前言:
[...] You can use functional programming techniques in C# to great benefit, though it is easier and more natural to do so in F#. [...] Functional programming is a state of mind. [...]
EDIT-2:
我觉得有必要进一步澄清这个问题。标题中提到的functional与Functional Programming严格相关;我不是在问更功能性的方法分组方式,即更逻辑的方式或更一般意义上的方式。
这意味着实现将尽可能多地遵循由 NOOO 总结的 FP 的创始概念。为了方便和清晰起见,在此引用声明:
- Functions and Types over classes
- Purity over mutability
- Composition over inheritance
- Higher-order functions over method dispatch
- Options over nulls
问题是关于如何根据 FP 概念编写 C# 库的布局,因此(例如)将方法放在数据结构中绝对不是一个选项;因为这是一个创始的面向对象范例。
EDIT-3:
此外,如果问题得到回应(和各种评论),我不想给人一种错误的印象,即有人说一种编程范式优于另一种。 和以前一样,我会在其 Expert F# 3.0(第 20 章 - 设计 F# 库 - 第 565 页)一书中提到 FP 的权威 Don Syme:
[...] It's a common misconception that the functional and OO programming methodologies compete; in fact, they're largely orthogonal. [...]
最佳答案
Note: If you want a shorter, more-to-the-point answer, see my other answer. I am aware that this one here might seem to ramble & go on forever & talk past your issue, but perhaps it will give you a few ideas.
如果不知道 Animal
之间的确切关系,很难回答您的问题和 Skeleton
.我将在回答的后半部分就这种关系提出建议,但在此之前,我将简单地赞同我在您的帖子中看到的内容。
首先我会尝试从您的代码中推断出一些事情:
static class AnimalTopology
{
// Note: I made this function `static`... or did you omit the keyword on purpose?
static IEnumerable<Animal> ListVertebrated(Func<Skeleton, bool> selector)
{
…
}
}
如果您根据功能原则设计了该功能,那么它应该没有副作用。也就是说,它的输出仅依赖于它的参数。 (在半面向对象的设置中,可能在
AnimalTopology
的其他静态成员上;但由于您没有显示任何内容,让我们忽略这种可能性。)如果函数确实没有副作用(并且不访问
AnimalTopology
的静态成员),那么函数的类型签名表明可以派生Animal
。来自Skeleton
,因为它接受作用于Skeleton
的东西s 和返回Animal
如果这也是真的,那么为了能够给出答案,让我假设如下:
class Skeleton { … public Animal Animal { get { … } } // Skeletons have animals!? We'll get to that. }
现在很明显你的函数无法实现,因为它可以派生Animal
来自 Skeleton
s,但它没有收到任何 Skeleton
根本;它只接收作用于 Skeleton
的谓词函数. (您可以通过添加类型为 Func<IEnumerable<Skeleton>> getSkeletons
的第二个参数来解决此问题,但是...)
在我看来,像下面这样的东西会更有意义:
static IEnumerable<Animal> GetVertebrates(this IEnumerable<Skeleton> skeletons,
Func<Skeleton, bool> isVertebrate)
{
return skeletons
.Where(isVertebrate)
.Select(s => s.Animal);
}
现在,您可能想知道为什么您要根据骨骼来猜测动物;并且不是 bool
属性“是脊椎动物”是动物(或骨骼)的固有属性吗?真的有几种方法可以决定这一点吗?
我建议如下:
class Animal
{
Skeleton Skeleton { get; } // not only vertebrates have skeletons!
}
class Vertebrate : Animal { … } // vertebrates are a kind of animal
static class AnimalsExtensions
{
static IEnumerable<Vertebrate> ThatAreVertebrates(this IEnumerable<Animal> animals)
{
return animals.OfType<Vertebrate>();
}
}
请注意上面扩展方法的使用。这是一个如何使用它的示例:
List<Animal> animals = …;
IEnumerable<Vertebrate> vertebrates = animals.ThatAreVertebrates();
现在假设您的扩展方法做了更复杂的工作。在这种情况下,将其放入自己指定的“算法类型”中可能是个好主意:
interface IVertebrateSelectionAlgorithm
{
IEnumerable<Vertebrate> GetVertebrates(IEnumerable<Animal> animals);
}
这具有可以设置/参数化的优点,例如通过类构造函数;并且您可以将算法拆分为几个方法,它们都位于同一类中(但都是 private
除了 GetVertebrates
。)
当然,您可以使用函数式闭包进行相同类型的参数化,但根据我的经验,这在 C# 设置中很快就会变得困惑。在这里,类是将一组功能组合在一起作为一个逻辑实体的好方法。
关于c# - 如何在 C# 函数库中正确划分代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15691047/