在使用函数式编程语言编写代码时,是否有任何已知的原则,最佳实践和设计模式?
答案 0 :(得分:20)
有折叠,展开,地图等。
我考虑使用它们的最佳实践,因为它很容易推理出他们的行为,并且他们经常传达函数的目的(举个例子,看看着名的Evolution of a Haskell Programmer和对比新生与大四和教授一起。)
答案 1 :(得分:10)
设计模式:让类型指导您的编码。
找出您要返回的类型。
知道某些类型构造函数具有某种语法,并利用它来使所需类型更小。以下是两个例子:
如果您尝试返回函数类型T1 -> T2
,始终可以安全地编写
\ x -> ...
现在你正在尝试生成T2
类型的值,这是一个较小的类型,另外你还获得了x
类型的额外值T1
,让你的工作更轻松。
如果lambda变得不必要,你可以随时将其减少。
如果您尝试生成一对(T1, T2)
类型,则始终可以尝试生成x
类型的值T1
和值y
类型T2
,然后形成对(x, y)
。同样,您已将问题简化为类型较小的问题。
一旦类型尽可能小,可以查看范围内所有let-bound和lambda-bound变量的类型,并了解如何生成所需类型的值。通常,您希望使用所有函数的所有参数;如果你不这样做,请务必解释原因。
在许多情况下,特别是在编写多态函数时,这种设计技术可以将复杂函数的构造减少到一两个选项。这些类型指导程序的构造,因此很少有方法可以编写正确类型的函数 - 通常只有一种方法没有明显错误。
答案 2 :(得分:8)
最佳实践:使用代数数据类型并利用模式匹配编译器中的穷举检查。特别是,
永远不要匹配顶级的通配符模式_
。
设置编译器选项,以便模式匹配中缺少的大小写是错误,而不是警告。
答案 3 :(得分:7)
不要遵循原则,遵循你的鼻子。保持功能简短。寻找降低代码复杂程度的方法,这通常但不一定意味着最简洁的代码。了解如何使用内置的高阶函数。
在编写函数后立即重构并减小函数的代码大小。这节省了时间,因为明天你不会有问题&解决方案在你的脑海里。
答案 4 :(得分:4)
设计模式:让编译器为您的函数推断类型,并确保这些类型完全符合您的预期。如果类型更具多态性或更少多态,请找出原因。
例如,如果您正在Haskell中编写排序函数,那么期待
Ord a => [a] -> [a]
如果您的类型是
Num a => [a] -> [a]
或
[a] -> b
然后出现了可怕的错误。
最佳实践:一旦您使用编译器确认类型符合您的预期,就会为每个顶级函数设置一个显式类型签名。 (或者,如果您使用的是ML或Caml,请编写显式接口。)设置编译器选项,使缺少签名的值触发错误。
答案 5 :(得分:3)
我的回答可能有点模糊:努力使你的代码美丽作为最重要的方面。致quote David Gelernter:
美丽在计算领域比其他任何技术都重要,因为软件非常复杂。美是对抗复杂性的最终防御。
和Brian Kernighan:
控制复杂性是计算机编程的本质。
如果您追求这个目标,它将引导您编写易于阅读并理解(这非常重要)的代码,将代码拆分为具有明确目的的小型紧凑部件。它将引导您学习如何以最佳方式表达您的想法。
(所有这些并不仅仅适用于函数式语言,但编写漂亮的代码对它们来说要容易得多。)
答案 6 :(得分:2)
Why Functional Programming Matters给出了很好的动机,为什么懒惰和高阶(一等)函数提供了很多功能较少的语言missing and supplement with design patterns。
在Haskell的上下文中,我认为本书Real World Haskell对习语和抽象以及类型类等有一些好的和实用的建议。 Typeclassopedia也总是有用的。核心的,非常抽象的类型类可以看作是设计模式,除了它们由编译器/类型系统和部分语言强制执行(如果你学习如何使用它们)。
在Lisp的上下文中,Paul Graham写了一本名为On Lisp(available online)的书,他表明函数式语言是创建自定义编程语言然后编写程序的理想选择。因此,嵌入式域特定语言本身就是一种设计模式。