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
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

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
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

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Thermometer Continuation in Scala
  • Deploying a Scala Play Application to Heroku: A Step-by-Step Guide
  • Upgrading Spark Pipelines Code: A Comprehensive Guide
  • Java vs. Scala: Comparative Analysis for Backend Development in Fintech

Trending

  • Develop a Reverse Proxy With Caching in Go
  • The 4 R’s of Pipeline Reliability: Designing Data Systems That Last
  • Unlocking AI Coding Assistants Part 1: Real-World Use Cases
  • Java's Quiet Revolution: Thriving in the Serverless Kubernetes Era
  1. DZone
  2. Coding
  3. Languages
  4. Predicate Composition in Scala

Predicate Composition in Scala

In this post, we present an encoding pattern for predicate composition as compensation to the language design. Our composition is Boolean algebra complete.

By 
Michael Wang user avatar
Michael Wang
·
Nov. 14, 21 · Analysis
Likes (2)
Comment
Save
Tweet
Share
5.9K Views

Join the DZone community and get the full member experience.

Join For Free

Function composition is a term used to define a process of a function mixing with other functions. During the composition one function holds the reference to another function. 

Scala language has two native ways to compose functions:

  • (f compose g) (x), which is equivalent to f(g(x))
  • (f andThen g) (x), which is equivalent to g(f(x))

However, as opposed to function composition, predicate composition has its own specifics and rules. Unlike function composition, where one function has a reference of another, predicates are independent; in addition, a predicate always has a return type of Boolean. The composition of predicates follows the laws of Boolean algebra. 

There are three basic Boolean predicates, AND, OR and NOT. The other Boolean predicates can be a composition of these three basic predicates. 

For various reasons, the Scala language does not have a native way to compose predicates as it does for functions. In this article, we present an encoding pattern for predicate composition as compensation to the language design. We demonstrate that our proposed composition is Boolean algebra complete.

Predicate in Scala

A predicate in Scala is a function that takes the following form:

Scala
 
T => Boolean


Which can be presented as:

Scala
 
Function[T, Boolean]


Thus, we define our predicate trait as:

Scala
 
trait Predicate[T] extends Function[T, Boolean]


In Scala, Function is a type alias of Function1[-A,+B] that takes one parameter of type A and returns a type of B. As we can see, Predicate takes a type of T and always return a type of Boolean. 

The three basic predicates, AND, OR, and NOT, can be defined as follows:

Scala
 
trait Predicate[T] extends Function[T, Boolean]{
  def or(p2:Predicate[T]) = OrPredicate[T](this, p2)
  def and(p2:Predicate[T]) =  AndPredicate[T](this, p2)
  def unary_! = NotPredicate[T](this)
}
case class OrPredicate[T](p1:Predicate[T], p2:Predicate[T]) extends Predicate[T] {
  def apply(a:T) = p1(a) || p2(a)
}
case class AndPredicate[T](p1:Predicate[T], p2:Predicate[T]) extends Predicate[T] {
  def apply(a:T) = p1(a) && p2(a)
}
case class NotPredicate[T](p:Predicate[T])  extends Predicate[T] {
  def apply(a:T) = !p(a)
}


As we can see, the predicate is a higher-order function, the function can be composed by its three basic Boolean operations OR, AND, and NOT (unary!)  to yield a new predicate.

Secondary operations, NAND, NOR, and XOR can be a composition of the three basic operations by the end-user, however, they can also be modeled as primitive operations as follows, which we’d like to include in our implementation:

Scala
 
case class NandPredicate[T](p1: Predicate[T], p2: Predicate[T])  {
  def apply(a: T) = !(p1(a) && p2(a))
}
case class NorPredicate[T](p1: Predicate[T], p2: Predicate[T])  {
  def apply(a: T) = !(p1(a) || p2(a))
}
case class XorPredicate[T](p1: Predicate[T], p2: Predicate[T])  {
  def apply(a: T) = (p1(a) || p2(a)) and !(p1(a) && p2(a))
}


In addition, we define the identity predicates for Or and And, which always yields false and true, respectively:

Scala
 
case class TruePredicate[T](r:T) extends Predicate[T] {
   def apply(a: T) = true
}
case class FalsePredicate[T](r:T) extends Predicate[T] {
   def apply(a: T) = false
}


Composition of the Predicates

Once the basic predicates, secondary predicates, and identity predicates are structured, we can define the type classes of our interest. For instance, for type Int:

Scala
 
implicit class IntEqualTo(x:Int) extends Predicate[Int] {
  def apply(a:Int) = a == x
}
implicit class IntGreaterThan(x:Int) extends Predicate[Int] {
  def apply(a:Int) = a > x
}


Composing these predicates can yield more complicated predicates. For instance, greater-than or equal-to Predicate can be composed as:  

IntGreaterThan and IntEqualTo

The predicates are composed into a new predicate without being evaluated, which makes the composition lazy. This can be important in large and complex rule-building and decision-making scenarios.

Completeness of Predicate Composition

Boolean Algebra, also known as Binary Algebra, deals with operations on logical values and incorporates binary variables. A predicate is a statement or mathematical assertion that contains variables, yields a Boolean value. 

Law of Boolean Algebra comprised of the following:

Monotone Laws

Associativity of Or

x or (y or x) = (x or y) or z

Associativity of And

x and (y and z) = (x and y) and z

Commutativity of Or

x or y = y or x

Commutativity of And

x and y = y and x

Distributivity of And over Or

X and (y or z) = (x and y) or (x and z)

Distributivity of Or over And

X or (y and z) = (x or y) and (x or z)

Identity for Or

X or false = x

Identity for And

X and true = x

Annihilator for And

X or true = true

Annihilator for Or

X and false = false

Idempotence of And

X and x = x 

Idempotence of Or

X or x = x

Absorption 1

X and (x or y) = x

Absorption 2

X or (x and y) = x


Nonmonotone Laws

Complementation 1

X and !x = false

Complementation 2

A or !x = true

Double Negative

! ( !x) = x

De Morgan 1

!x and !y = !(x or y)

De Morgan 2

!x or !y = !(x and y)


We demonstrate that our proposed composition satisfies all the laws of Boolean Algebra. The demonstration and a sample implementation can be found at:

Predicate Composition

Conclusion 

In this article, we proposed an encoding pattern for predicate composition in Scala, as compensation for the lack of native support in the language design. We demonstrated that the proposed pattern satisfies the law of Boolean algebra. This pattern can be efficiently used in large-scale application development involving complex predicates, building dynamic rules, and making lazy evaluations in decision making.

Scala (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • Thermometer Continuation in Scala
  • Deploying a Scala Play Application to Heroku: A Step-by-Step Guide
  • Upgrading Spark Pipelines Code: A Comprehensive Guide
  • Java vs. Scala: Comparative Analysis for Backend Development in Fintech

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

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

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!