DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
Securing Your Software Supply Chain with JFrog and Azure
Register Today

Trending

  • Grow Your Skills With Low-Code Automation Tools
  • Opportunities for Growth: Continuous Delivery and Continuous Deployment for Testers
  • Building the World's Most Resilient To-Do List Application With Node.js, K8s, and Distributed SQL
  • What Is JHipster?

Trending

  • Grow Your Skills With Low-Code Automation Tools
  • Opportunities for Growth: Continuous Delivery and Continuous Deployment for Testers
  • Building the World's Most Resilient To-Do List Application With Node.js, K8s, and Distributed SQL
  • What Is JHipster?
  1. DZone
  2. Coding
  3. Languages
  4. Eta-Expansion and Partially Applied Functions in Scala

Eta-Expansion and Partially Applied Functions in Scala

Daniel Ciocirlan user avatar by
Daniel Ciocirlan
CORE ·
Sep. 07, 20 · Tutorial
Like (3)
Save
Tweet
Share
3.64K Views

Join the DZone community and get the full member experience.

Join For Free

This article is for Scala programmers who know at least these essential concepts: what a method is and how to define a function value (lambda). Here we'll discuss the topic of eta-expansion and partially-applied functions, which are often loosely covered and piecemeal.

You can read this article over at the Rock the JVM blog in its original form or watch as video on YouTube or in the video below:


Background

Long story short, methods and functions are different things in Scala. When I write

Scala
xxxxxxxxxx
1
 
1
def incrementMethod(x: Int): Int = x + 1


that's a method, which is a piece of code that can be invoked on an instance of a class. A method is a member of the enclosing class or object and can be invoked like this:

Scala
 
xxxxxxxxxx
1
 
1
val three = someInstance.incrementMethod(2)
2

          


Even if you call the method from the body of the class or object, it's the same as invoking it on the this instance. So you won't be able to call a method on its own, because it's tied to some instance of a class.

Function values (aka lambdas), on the other hand, are pieces of code that can be invoked independently of a class or object. Functions are assignable to values or variables, can be passed as arguments and can be returned as results - it's one of the most important tenets of functional programming in Scala.

Scala
xxxxxxxxxx
1
 
1
val incrementFunction = (x: Int) => x + 1
2
val three = incrementFunction(2)


Behind the scenes, these function values are actually instances of the FunctionN family of traits with an apply method which benefits from special treatment, so what you're doing is in fact:

Scala
xxxxxxxxxx
1
 
1
// what the compiler does
2
val incrementFunction = new Function1[Int, Int] {
3
    override def apply(x: Int): Int = x + 1
4
  }
5
val three = incrementFunction.apply(2) // desugared from incrementFunction(2)


Methods and functions are thus different in Scala. However, because the user sees and uses them in the same way (just invoke them), they're "morally" equivalent. The eta-expansion mechanism allows the conversion between a method and a function.

Converting a Method to a Function

Because a method and a function are seen differently by the JVM - a method of a class vs a field of type FunctionN - you can't simply say

Scala
 




xxxxxxxxxx
1


 
1
val incrementF = incrementMethod
2

          



because the compiler will think you'll try to call your increment method, which requires arguments. The way you'd do the conversion is

Scala
xxxxxxxxxx
1
 
1
val incrementF = incrementMethod _


The underscore at the end is a signal for the compiler that you want to turn the method into a function value, and you'll obtain a function of type Int => Int. This conversion is called eta-expansion, and the compiler will generate a piece of code that will look something like

Scala
xxxxxxxxxx
1
 
1
val incrementF = (x: Int) => incrementMethod(x)


The compiler can also do this automatically if you give it the function type in advance:

Scala
 
xxxxxxxxxx
1
 
1
val incrementF2: Int => Int = incrementMethod


In this case, the compiler can disambiguate the context, because you declared that you want a function so the compiler will automatically eta-expand the method for you.

Partially Applied Functions

Another important scenario where eta-expansion is useful is with methods taking multiple argument lists.

Scala
xxxxxxxxxx
1
 
1
def multiArgAdder(x: Int)(y: Int) = x + y
2
val add2 = multiArgAdder(2) _


In this case, you'll get another function which takes the remaining arguments, therefore of type Int => Int. This is called a partially applied function, because you're only supplying a subset of argument lists and the remaining arguments are to be passed later:

Scala
xxxxxxxxxx
1
 
1
val three = add2(1)


In a similar fashion as before, the compiler can detect whether a value is expected to have a function type, and so it can automatically eta-expand a method for you:

Scala
xxxxxxxxxx
1
 
1
List(1,2,3).map(multiArgAdder(3)) // eta-expansion is done automatically


In this case, the argument of the map method needs to have the type Int => Int, so the compiler will automatically turn the method into an eta-expanded lambda for you.

Interesting Questions

So far, we've discussed only methods that have a single argument in their list. Here's something to think about:

Scala
xxxxxxxxxx
1
 
1
def add(x: Int, y: Int) = x + y
2
val addF = add _


In this case, the method has two arguments. An eta-expanded function value (lambda) will have two arguments as well, so it will be of type (Int, Int) => Int. A similar expansion will happen on a larger number of arguments as well.

Another interesting scenario is: what happens on more than two argument lists and/or we're left with more than an argument list in the expanded function:

Scala
xxxxxxxxxx
1
 
1
def threeArgAdder(x: Int)(y: Int)(z: Int) = x + y + z
2
val twoArgsRemaining = threeArgAdder(2) _


In this case, we'll get a curried function which will take the remaining argument lists in turn, so the function type will be Int => Int => Int. If you want to invoke it:

Scala
xxxxxxxxxx
1
 
1
val ten = twoArgsRemaining(3)(5)


At the same time, if we pass more than one argument list:

Scala
xxxxxxxxxx
1
 
1
val oneArgRemaining = threeArgAdder(2)(3) _


then we'll get a function which takes the remaining argument lists (a single integer), so it will have the type Int => Int.

To put it short, eta-expansion turns a method into a function which will take the remaining argument lists (however large) in turn, however long the chain may be.

Conclusion

We covered eta-expansion and its application in defining function values converted from methods and in partially applied functions.

Check out the Rock the JVM blog for more articles like this and our YouTube channel for their video versions. For fresh updates, follow us on Twitter and LinkedIn!

Scala (programming language)

Published at DZone with permission of Daniel Ciocirlan. See the original article here.

Opinions expressed by DZone contributors are their own.

Trending

  • Grow Your Skills With Low-Code Automation Tools
  • Opportunities for Growth: Continuous Delivery and Continuous Deployment for Testers
  • Building the World's Most Resilient To-Do List Application With Node.js, K8s, and Distributed SQL
  • What Is JHipster?

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com

Let's be friends: