关于可重用库(针对我正在学习的内容)的FP设计的主要区别之一是,这些库比对应的OO(通常)更以数据为中心。

Tomas Petricek在this博客文章中对此进行了很好的解释,这似乎也从 TFD (类型优先开发)等新兴技术中得到了证实。

如今,语言是多范式的,其书中的相同Petricek解释了可用于C#的各种功能技术。

我在这里感兴趣的是,因此是一个问题,即如何正确分割代码

因此,我使用等效的联合定义了库数据结构(如Petricek书中所示),并且我计划根据矿山需求的领域逻辑将它们与不可变的列表和/或元组一起使用。

我应该在哪里放置对数据结构起作用的操作(方法...函数)?

如果我想定义一个使用标准委托(delegate)Func<T1...TResult>中包含的函数值的高阶函数,该将其放置在哪里?

常识说我可以将这些方法归为静态类,但是我想得到已经用C#编写过函数库的人们的确认。

假设这是正确的,并且我有一个像这样的高阶函数:

static class AnimalTopology {
  IEnumerable<Animal> ListVertebrated(Func<Skeleton, bool> selector) {
    // remainder omitted
  }
}

如果选择脊椎动物要在库中显示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的两个短语,Mads Torgersen的Real World Functional Programming前言:



EDIT-2:
  • 我觉得有必要进一步澄清这个问题。标题中提到的功能严格与Functional Programming相关;我不是在要求更多的逻辑方式或通常更有意义的方式,而不是分组方法的更实用的方式。
  • 这意味着该实现将尝试尽可能多地遵循NOOO宣言概述的FP创始概念,并在此处引用以方便和清楚:



  • 问题围绕,如何布局遵循FP概念编写的C#库,因此(例如)绝对不是将方法放入数据结构中的选择。因为这是一个创始的面向对象范例。

    EDIT-3:

    同样,如果问题得到了回应(以及各种评论),我也不想给人以错误的印象,即曾经有人说一种编程范例比另一种编程范例优越。
    如前所述,我将在FP F 3.0版专家书(第20章-设计F#库-第565页)中提到FP的一个权限:Don Syme:

    最佳答案



    不知道AnimalSkeleton之间的确切关系很难回答您的问题。在回答的后半部分,我将对这种关系提出建议,但在此之前,我将简单地与您在帖子中看到的保持一致。

    首先,我将尝试从您的代码中推断出几件事:

    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,但是它根本不接收任何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#设置中很快会变得困惑。在这里,类是将一组功能组合在一起作为一个逻辑实体的好方法。

    10-06 05:21