What kinds of functions are considered as “composable”?

喜欢而已 提交于 2020-01-02 06:15:10

问题


The Wikipedia article Function composition (computer science) says:

Like the usual composition of functions in mathematics, the result of each function is passed as the argument of the next, and the result of the last one is the result of the whole.

I have two questions about it:

  1. A composable function must have both arguments and return value?

    So following functions are not:

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

    Is my understanding correct?

  2. Can this function side-effect?

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

    Do we still consider it as "composable"?


回答1:


The mixture of formal language from math with more colloquial language from programming makes these conversations difficult. You're dealing with two contextually-loaded words here: "composable" and "function".

Function composition — in math

The mathematical notion of a "function" A → B is a mapping from some set A to some set B, and "function composition" is a specific operation denoted by . For some f: A → B and g: B → C, g∘f is a function A → C such that (g∘f)(x) = g(f(x)) for all x in A. This composition is defined for any two functions if their domain/codomain match up in this way (in other words, such a pair of functions "can be composed"), and we describe this by stating that "functions are composable".

Composability — in programming

As a qualitative term, we use "composability" often in software to describe the ability of a set of compositions can build large things from combining small ones. In this sense, programmers describe functions (as a whole) as "very composable", because functions can (and, in a purely functional language like Haskell, do) comprise the large and the small of an entire program.

In software we also see a more human-oriented usage of the term "composable" which tends to be associated with "modularity". When components are stateless, concerns are separated, and APIs have low surface area, it's easier to compose programs without making mistakes. We praise the components of such a design as being "composable"—not just because they can be combined, but because they're easy to combine correctly.

Function — in programming

I'm going to use the slightly outdated term "subroutine", because I don't know a good way to discuss this in the parlance of our times. If a subroutine doesn't do any IO (and always halts, and doesn't throw…), then it implements (or "is") a "function" in the mathematical sense. IO subroutines have superficial resemblance to functions, because they may have input and output values, but the similarity stops there. None of the conversations we may have about "function composition" as we first discussed it will apply.

Here's where we hit the trickiest linguistic difficulty, because the word "function" has come into common usage to refer to any subroutine, even ones that perform IO. FP enthusiasts tend to fight this—people say things like "if it does IO, it isn't a function"—but that battle of popularity has been lost and there's no turning back now. Within most programming contexts, all subroutines are called "functions", and the only way to distinguish functions that satisfy the mathematical definition is to call them "pure functions".


With these definitions established, let's address your questions:

"A composable function must have both arguments and return value?"

There are a couple boring things to point out about this question. First, every function in Scala technically has a return type. If that type is Unit, it may be elided for brevity, but it's still a return type.

And a nullary (0-arg) function can be trivially transformed into an equivalent function with an argument. So really, it just doesn't matter. If you're in a situation where you need to compose functions with arguments and f has no argument, you can just write _ => f.

"Can this function have side-effect?"

Merely a semantic squabble. In the context of Scala, the most appropriate thing to say is that it is a Function (or perhaps technically a "method", depending on where it is defined), but due to the side effect, it is not a pure function.

"Do we still consider it as 'composable'?"

Sort of. All of these things still "come together" in a fairly general way, so yes, they do compose in the software sense. Although pure functions compose better than impure ones. And the mathematical notion of function composition does not apply to subroutines that are not pure functions.

Finally, if you want to know whether they literally compose in Scala with the compose method on Function1, you don't need Stack Overflow; just ask the compiler.




回答2:


2) If function has side effect - you can't consider it as a function

1) if function have no arguments - it's a constant. If function have no return value - its return value is Unit (which also can be an input argument)

P.S. You can define a "function" (subroutine) composition for "dirty" functions as well, but it's not what people usually mean when talking about this; as functional composition in mathematics means composition of pure functions.


Talking about Scala:

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 is not composable here as they suppose that they probably have side-effects. However, approach with Unit works here as it gives you 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>


来源:https://stackoverflow.com/questions/31444762/what-kinds-of-functions-are-considered-as-composable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!