什么类型的功能被视为"可组合"?

时间:2015-07-16 03:10:43

标签: function scala functional-programming

维基百科文章Function composition (computer science)说:

  

与数学中通常的函数组合一样,每个函数的结果作为下一个函数的参数传递,最后一个函数的结果是整体的结果。

我有两个问题:

  1. 可组合函数必须同时具有参数和返回值吗?

    以下功能不是:

    def doNothing(): Unit = ()
    def myName(): String = "My name"
    def eat(food:String): Unit = ()
    

    我的理解是否正确?

  2. 这个功能可以副作用吗?

    def hello(name:String):String = {
      println("name: " + name) // side-effect
      name + "!"
    }
    

    我们是否还将其视为"可组合"?

2 个答案:

答案 0 :(得分:6)

来自数学的正式语言与来自编程的更通俗语言的混合使得这些对话变得困难。你在这里处理两个上下文加载的单词:"可组合"和"功能"。

功能组合 - 数学

"功能的数学概念" A → B是从某个集合A到某个集合B的映射,以及"函数组合"是表示的特定操作。对于某些f: A → Bg: B → Cf∘g是一个函数A → C(f∘g)(x) = f(g(x))x中的所有A。如果它们的域/ codomain以这种方式匹配(换句话说,这样的一对函数"可以组成"),则为任何两个函数定义该组合,并且我们通过声明&#34来描述这个。 ;函数是可组合的"。

可组合性 - 编程

作为定性术语,我们使用"可组合性"通常在软件中描述一组组合物的能力可以通过组合小组合来构建大的东西。从这个意义上讲,程序员将函数(作为一个整体)描述为"非常可组合的",因为函数可以(并且,在像Haskell这样的纯函数式语言中)构成整个程序的大小。

在软件中,我们也看到了一个更加以人为本的术语用法"可组合的"这往往与"模块化"有关。当组件处于无状态时,关注点被分离,API的表面积较小,编写程序更容易而不会出错。我们称赞这样一个设计的组成部分是“可组合的” - 不只是因为它们可以组合在一起,而是因为它们很容易到正确组合

功能 - 编程

我将使用稍微过时的术语"子程序",因为我不知道用我们这个时代的说法讨论这个问题的好方法。如果一个子程序没有做任何IO(并且总是停止,并且不会抛出......),那么它实现(或"是")a"功能"在数学意义上。 IO子程序与函数表面相似,因为它们可能具有输入和输出值,但相似性在那里停止。我们可能没有关于"功能组合"正如我们首先讨论的那样,它将适用。

在这里,我们遇到了最棘手的语言难度,因为“" function"已经普遍用于引用任何子例程,甚至是执行IO的子例程。 FP爱好者倾向于对抗这种情况 - 人们会说像这样的事情,如果它做了IO,它就不是一种功能 - 但是人气之战已经失去了,现在还没有回头路。在大多数编程上下文中,所有子例程都被称为" functions",区分满足数学定义的函数的唯一方法是将它们称为"纯函数"。

考虑到这一背景,我恭敬地声称你的问题相当无趣,如果我们要从这次讨论中获得任何价值,他们需要额外的动力和背景。

"可组合函数必须同时包含参数和返回值?"

有一些无聊的事情要指出这个问题。首先,Scala中的每个函数在技术上都具有返回类型。如果该类型为Unit,则可能会因为简洁而省略,但它仍然是返回类型。

一个nullary(0-arg)函数可以通过参数简单地转换为等价函数。所以真的,它并不重要。如果您需要使用参数组合函数并且f没有参数,则可以只编写_ => f

"此功能是否有副作用?"

仅仅是一种语义争吵。在Scala的上下文中,最合适的是它是Function(或者在技术上可能是"方法",取决于它的定义位置),但是由于副作用,它不是一个纯粹的功能。

"我们仍然认为它是可组合的'?"

排序。所有这些事情仍然在一起#34;以相当一般的方式,所以是的,它们确实在软件意义上构成。虽然纯函数比不纯函数更好。函数组合的数学概念不适用于非纯函数的子程序。

最后,如果您想知道他们是否在Function1上使用compose方法在Scala中完全构成,那么您就不需要Stack Overflow;请问编译器。

答案 1 :(得分:1)

2)如果功能有副作用 - 您不能将其视为功能

1)如果函数没有参数 - 它是一个常数。如果function没有返回值 - 它的返回值是Unit(也可以是输入参数)

P.S。您可以定义一个"功能" (子程序)组成"脏"功能也是如此,但这并不是人们在谈论这个时通常的意思;因为数学中的功能组合意味着纯函数的组合。

谈论斯卡拉:

scala> def doNothing(): Unit = ()
doNothing: ()Unit

scala> (doNothing _)
res0: () => Unit = <function0>

scala> (doNothing _) andThen (doNothing _)
<console>:9: error: value andThen is not a member of () => Unit
              (doNothing _) andThen (doNothing _)
                            ^

scala> def doSomething(a: Int) = a
doSomething: (a: Int)Int

scala> (doSomething _) andThen (doSomething _)
res2: Int => Int = <function1>

function0在这里是不可组合的,因为他们认为它们可能有副作用。但是,Unit的方法适用于function1

scala> def eat(food:String): Unit = ()
eat: (food: String)Unit

scala> (eat _) andThen (doNothing _)
<console>:10: error: type mismatch;
 found   : () => Unit
 required: Unit => ?
              (eat _) andThen (doNothing _)
                               ^

scala> def doNothingU(u: Unit): Unit = ()
doNothingU: (u: Unit)Unit

scala> (doNothingU _) andThen (doNothingU _)
res5: Unit => Unit = <function1>

scala> (eat _) andThen (doNothingU _)
res6: String => Unit = <function1>

scala> (doNothingU _) compose eat
res11: String => Unit = <function1>