DRY code with Higher Order Function in Scala – Part I
DRY Code with Higher-order Function in Scala – Part I
In Software Engineering, don’t repeat yourself (DRY) is a principle of software development, aimed at reducing repetition of information of all kinds, especially useful in multi-tier architectures – Wikipedia
Introduction
DRY is “Don’t repeat yourself”
This is a term which is used to tell people to write code that is reusable, so that you don’t end up writing similar code over and over again.
There are many ways to reduce duplication code, but in this post I am going to introduce one of fundamentals of functional programming which was called as Higher-order function, specifically in Scala language.
What is Higher-order Function
Scala allows the definition of higher-order functions. These are functions that take other functions as parameters, or whose result is a function.
def calculate(f: Int => Int, a: Int, b: Int) =
if (b != 0) f(a, b) else throw Exception("Divide by 0")
def divide(a: Int, b: Int) = a / b
calculate(divide, 2, 0) // => Exception
calculate(divide, 4, 2) // => 2
calculate()
is a higher-order function which take one function f()
and two params a: Int
b: Int
then return a function f(a, b)
or an exception.
Useful higher-order function
You may familiar with some higher-order functions already such as filter, map, exist… of Scala collection.
For example, let’s look at what filter function was defined:
def filter(f: A => Boolean)
Function filter
take one function f
as parameter and it’s up to you to define what function f
will be, as long as the output is boolean.
def isEven(x: Int): Boolean = x % 2 == 0
val numbers = Seq(1, 2, 3, 4, 5)
numbers.filter(x => isEven(x)) // filter(Int => Boolean)
numbers.filter(isEven) // isEven: Int => Boolean
These function of Scala’s collection was built for reducing duplication code.
Instead of creating your own method or using for loop through any element of collection, you can easily define your function as a statement and pass it into a higher-order function of Scala and you’re staying DRY with your code.
The real-world Example
All above is just a theory, it’s quite boring.
Ok, let’s diving into the real-world example to see how’s it work.
First order function
In Play Framework, any Controller was created for process request from client-side.
UserController
was implement as bellow.
/**
* UserController
*/
def createUser() = Action { implicit request =>
userForm.bindFromRequest.fold(
formWithErrors => {
// binding failure, you get error here
// handleError()
},
userData => {
// binding success, you get the actual value
// createUser()
}
)
}
def updateUser() = Action { implicit request =>
userForm.bindFromRequest.fold(
formWithErrors => { /* code */ },
userData => { /* code */ }
)
}
Function createUser()
and updateUser()
take a Form from user, validate it then handle any error if we got binding failure or process if it has valid data.
Now I have PostController
too, it has similar purpose but for post creation.
/**
* PostController
*/
def createPost() = Action { implicit request =>
postForm.bindFromRequest.fold(
formWithErrors => {
// handleError()
},
postData => {
// createPost()
}
)
}
The more Controller we have the more piece of code form validation we need. And this will be repeated again and again.
It’s WET !!! ( “Write Everything Twice”, “We Enjoy Typing” or “Waste Everyone’s Time”.)
It’s time for refactoring, for staying DRY and mastering higher-order function.
/**
* FormValidator
*/
def withValidatedForm[T](form: Form[T])
(f: => T => Result) = Action {
implicit request => {
form.bindFromRequest.fold(
hasError => {
// You can handle error here.
// For example: Return BadRequest
BadRequest(hasError.errors.head.key)
},
formData => f(formData)
)
}
}
We created a higher order function and named it as withValidatedForm
.
It takes a Form
with type [T]
and function f
as input. Function f
return other function that mapping from type T
to Result
.
Inside this function, we normally validate the form, handle error if necessary or pass form’s data into function f
to continue process where it was called.
Now in UserController
we will use our higher-order function which just was defined.
/**
* UserController
*/
/*
Function createUser
@userForm: Form[UserForm]
return [Result]
*/
def createUser() = withValidatedForm(userForm) {
userData => // we got form data here
}
def updateUser() = withValidatedForm(userForm) {
userData => // continue process with this data
}
We checked form data in withValidatedForm(form)
and do the rest in an anonymous function (formData => Result)
. (function of Controller in Play FW must return type Result
)
withValidatedForm(form)(formData => Result)
It’s better, no more duplicated validation form code and seem more expressive.
The main idea is when we have function
f1()
,f2()
,f3()
… which has same logic like checking a statement, validating a request… we will put all the similar business into a functionh()
. And instead of repeat all the logic in any function, we will call higher-order functionh()
and pass the rest logic into itself.
def h(f: A => B) = if (logic == true) f else handleError
def f1 = h {
A => // Only valid case
}
def f2 = h {
A => // Process only the different logic
}
def f3 = h {
A => // return type B here
}
Other example
Before each request, we need to check user was logged in or not. If user was authenticated , we will continue process, if not we send an alert invalid request.
Firstly, we create our higher-order function for checking request:
def authenticated(f: Request[AnyContent] => Result) = Action { request =>
if (isAuthorized)
f(request) // Process request
else
Unauthorized // Alert unauthorized
}
We will validate request in this function, the common logic here is checking user was logged in or not (isAuthorized
) and put the other logic into function f
which was defined later.
And then we will use it in PostController
:
/**
* PostController
*/
def getAllPost() = authenticated { request =>
// Only logged user can see posts
}
def getPostById(id: Long) = authenticated { request =>
// Only logged user can see post
}
All the examples above is basically, but it’s show the helpful of higher-order function.
We can make not only a simple higher-order but also a very complicated function by compositing. It depends on you, however the more your composition function you have, the more complex code you see.
Anyway, keep in mind that: Don’t Repeat Yourself.
To be continued…
Reference
- http://www.scala-lang.org/api/current
- https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
- https://www.playframework.com/documentation/2.5.x/ScalaForms