Over the past time, people have summed up many common patterns and best practices for the design API. In general, we can always summarize some of the development examples from Apple's Foundation, Cocoa, Cocoa Touch, and many other frameworks. There is no doubt that different people have different opinions about how the API should be designed in a particular context, and there is much room for discussion on this issue. For many objective-c developers, however, the usual patterns have long been taken for granted.
With the advent of Swift, the design API has caused more problems. In the vast majority of cases, we can only continue to do the work at hand and then translate the existing method into the Swift version. This is not fair for swift, however, because Swift adds a lot of new features compared to objective-c. Quote from Swift founder Chris Lattner:
Swift introduces the idea of generics and functional programming, greatly extending the design space.
In this a
func composeFilters(filter1: Filter, filter2: Filter) -> Filter {
return { img in filter2(filter1(img)) }
}
composeFiltersThe two parameters of the function are filter, and a new filter filter is returned. The assembled filter requires aCIImagetype of parameter, and this parameter is passed to and fromfilter1eachfilter2. Now we cancomposeFiltersdefine our own combination filters:
let myFilter = composeFilters(blur(blurRadius), colorOverlay(overlayColor))
let result = myFilter(image)
We can also further define a filter operator to make the code more readable,
infix operator >|> { associativity left }
func >|> (filter1: Filter, filter2: Filter) -> Filter {
return { img in filter2(filter1(img)) }
}
operatorinfixis defined by a keyword to indicate that the operator has左and右two parameters.associativity leftindicates that this operation satisfies the left associative law, namely: F1 >|> F2 >|> F3 equivalent to (F1 >|> F2) >|> F3. By making this operation satisfy the left-binding law, plus the left-hand filter applied in the operation, the filter order is left-to-right when used, just like a Unix pipeline.
The remainder is a function, the content is the same as thecomposeFiltersbasic, except that the function name becomes>|>.
Next we apply this combo filter operator to the previous example:
let myFilter = blur(blurRadius) >|> colorOverlay(overlayColor)
let result = myFilter(image)
Operators make the code easier to read and understand the order in which the filters are used, and are more convenient when invoking filters. It's like being1 + 2 + 3 + 4add(add(add(1, 2), 3), 4)clearer and easier to understand.
Custom operators
Many objective-c developers are skeptical about custom operators. When Swift was released, it was a feature that was not very popular. Many people in C + + have experienced the overuse of custom operators (and even abuse), some of which have been experienced by individuals, and others that have been talked about.
You might have the same skepticism about the operator you defined earlier>|>, after all, wouldn't it be hard to understand if everyone defined their own operators? Thankfully there are a lot of operations in functional programming, and defining an operator for these operations is not a very rare thing.
The filter combination operator that we define is an example of a combination of functions, a concept that is widely used in functional programming. In mathematics, two functionsfand combinations aregsometimes written to define af ° gcompletely new function that maps the inputxto thef(g(x))top. This happens to be the>|>work we do (except for the inverse invocation of the function).
Generic type
To think about it, there's no need to define an operator to specifically assemble the filter, and we can use a generic operator to assemble the function. What we have now>|>is this:
func >|> (filter1: Filter, filter2: Filter) -> Filter
Once this is defined, the parameters we pass in are onlyFiltertypes of filters.
However, we can use the common features of Swift to define a generic function combination operator:
func >|> <A, B, C>(lhs: A -> B, rhs: B -> C) -> A -> C {
return { x in rhs(lhs(x)) }
}
This may be hard to understand at first-at least for me. But after looking at each part separately, everything becomes clear.
First, let's look at the angle brackets that follow the function name. The angle brackets define the generic type that the function applies to. In this example we define three types: A, B, and C. Because we don't specify these types, they can represent anything.
Let's take a look at the parameters of the function: The first argument: LHS (abbreviation for left-hand side), which is a function of type a-B. This means that a function has a parameter of a, and the return value is of type B. The second parameter: RHS (abbreviation for right-hand side), is a function of type B-C. The parameters are named LHS and RHS, because they correspond to the left and right values of the operators, respectively.
After rewriting the noFilterfilter combination operator, we quickly discovered that the previously implemented combination operator was only a special case in a generic function:
func >|> (filter1: CIImage -> CIImage, filter2: CIImage -> CIImage) -> CIImage -> CIImage
Replace the generic type A, B, and C in our mind so thatCIImageit is clear how useful it is to substitute the filter combination operator with a common operator.
Conclusion
So far, we have successfully encapsulated it with a functional APICore Image. Hopefully this example is a good illustration of a completely different world for objective-c developers than the design patterns of our familiar API. With Swift, we can now explore new areas and make full use of them.
More articles under the topic #16
Original functional APIs with Swift
About translators
Swift's functional API (RPM)