As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center提供指导。




7年前关闭。




下个月,我将进行一个新的R&D项目,该项目将采用一种功能编程语言(我投票支持Haskell,但现在F#得到了更多共识)。现在,我已经使用这种语言一段时间了,并与它们一起开发了一些命令行工具,但这是一个相当大的项目,我正在尝试提高我的函数式编程知识和技术。我也已经阅读了很多有关该主题的文章,但是在函数式编程世界中,找不到任何有关反模式的书籍或资源。
现在,了解反模式意味着了解其他聪明人失败:在OOP中,我认识了其中的一些失败者,并且我有足够的经验去明智地选择通常是反模式的东西完全适合我的需求。但是我可以选择它,因为我知道其他聪明人吸取的教训。
因此,我的问题是:函数编程中是否有文档化的 anti-patterns?直到现在,我所有的同事都告诉我他们什么都不知道,但是他们不能说出原因。
  • 如果可以,请包含一个指向权威资源(目录,论文,书籍或同等内容)的单个链接。
  • 如果否,请通过a proper theorem支持您的回答。

  • don't turn this question in a list:这是一个 bool(boolean) 型问题,只需要证明即可评估答案。例如,如果您是Oleg Kiselyov,则"is"就足够了,因为每个人都可以找到有关该主题的文章。不过,请大方。
    请注意,我正在寻找正式的反模式,而不是简单的不良习惯或不良做法。
    linked wikipedia article on Anti-Patterns:

    此外,“已记录”是指权威作者知名来源的内容。
    我习惯的语言是:
  • Haskell(我真的开始认为如果代码可以编译,它就可以工作!)
  • Scala
  • F#

  • 但是我也可以改编其他功能语言中记录的有关反模式的知识。
    我在网上进行了很多搜索,但是我发现的所有资源都与OOP或函数布局有关(在函数的开头定义变量,等等)。

    最佳答案

    我见过的唯一反模式是过单化,而且由于单子(monad)非常有用,因此介于不良做法和反模式之间。

    假设您希望某些对象具有某些属性P。您可以用P monad装饰对象(在Scala中,在REPL中使用paste可以使对象及其伴侣粘在一起):

    class P[A](val value: A) {
      def flatMap[B](f: A => P[B]): P[B] = f(value)       // AKA bind, >>=
      def map[B](f: A => B) = flatMap(f andThen P.pure)   // (to keep `for` happy)
    }
    object P {
      def pure[A](a: A) = new P(a)                        // AKA unit, return
    }
    

    好吧,到目前为止一切都很好。我们通过将value设置为val而不是将其设置为逗号来作弊(如果这是我们想要的),但是现在我们有了一个方便的包装器,可以在其中包装任何东西。现在,假设我们还有属性QR
    class Q[A](val value: A) {
      def flatMap[B](f: A => Q[B]): Q[B] = f(value)
      def map[B](f: A => B) = flatMap(f andThen Q.pure)
    }
    object Q {
      def pure[A](a: A) = new Q(a)
    }
    class R[A](val value: A) {
      def flatMap[B](f: A => R[B]): R[B] = f(value)
      def map[B](f: A => B) = flatMap(f andThen R.pure)
    }
    object R {
      def pure[A](a: A) = new R(a)
    }
    

    因此,我们装饰对象:
    class Foo { override def toString = "foo" }
    val bippy = R.pure( Q.pure( P.pure( new Foo ) ) )
    

    现在,我们突然面临许多问题。如果我们有一个需要属性Q的方法,那我们该怎么办呢?
    def bar(qf: Q[Foo]) = qf.value.toString + "bar"
    

    好吧,显然bar(bippy)无法正常工作。有traverseswap操作可有效翻转monad,因此,如果我们以适当的方式定义swap,我们可以做类似的事情
    bippy.map(_.swap).map(_.map(bar))
    

    返回我们的字符串(实际上是R[P[String]])。但是,我们现在致力于对调用的每种方法执行类似的操作。

    这通常是错误的事情。如果可能,您应该使用其他同样安全的抽象机制。例如,在Scala中,您还可以创建标记特征
    trait X
    trait Y
    trait Z
    val tweel = new Foo with X with Y with Z
    def baz(yf: Foo with Y) = yf.toString + "baz"
    baz(tweel)
    

    ew!这么容易。现在,非常重要的一点是要指出,并非一切都容易。例如,使用这种方法,如果您开始操作Foo,则必须自己跟踪所有装饰器,而不是让monadic map / flatMap为您代劳。但是很多时候您不需要进行大量的实物操作,然后深度嵌套的monad就是一种反模式。

    (注意:单子(monad)嵌套具有堆栈结构,而特征具有集合结构;没有内在的原因导致编译器不允许类似集合的单子(monad),但是对于类型理论的典型表述,它不是自然的构造。反模式是这是使用深层堆栈非常棘手的事实的简单结果。如果您为monad(或Haskell中标准的Monad转换器集合)实现所有Forth堆栈操作,则它们可能会更容易一些。

    07-27 14:00