Algebraic – in Java?

java

Published: 2021-01 (January 2021)
Relevant Java Version: Java 15.

Algebraic – a term that is often associated with school mathematics, and is commonly understood as a branch of mathematics which deals with symbols and rules for operating such symbols. The symbols usually represent quantities without fixed values (referred to as variables).

In programming languages, it carries a similar meaning. An algebraic data type is a composite containing variables. a composite can further contain other types as variables as well. A recursive type can contain another instance of itself as a variable. Algebraic refers to the property that an Algebraic Data Type is created by algebraic operations. The algebra discussed, is sums, products and patterns.

Algebraic data types were introduced in Hope, a small functional programming language developed in the 1970s at the University of Edinburgh.

Delving into Algebra

Sum Types

A sum type:

  • represents alternation (for three values A, B, C → A or B or C but not any combination or other subset).
  • defines variants
  • is a logical OR operator, only one of the variants is possible.

Product Types

A product type:

  • represents combination (for three values A, B, C → A and B and C, possible to hold empty for one or more).
  • holds values
  • is a logical AND operator

Pattern matching

Pattern matching is the check of a given sequence of tokens for presence of the constituents of some pattern. The match has to be exact without ambiguity, so has to evaluate to either is a match or is not a match.

What has algebra got to do with Java ?

Java has primitive as well as non-primitive data types.

Primitive: boolean, byte, char, short, int, long, float and double.
Non-Primitive: String, array, Object, composite objects etc.

This blog will cover some samples of algebraic or composite non-primitive data types in Java.

Algebra in Java

Sum types

Enum

Enumerations (enum) are a special sum type. Enums cannot have additional data associated with them once instantiated.

Enums can have final attributes that can be set via constructors and can have methods defined that can access such final attributes.

An enum can declare abstract methods which must then be implemented by each variant. Similarly, an enum can implement an interface, but each variant must implement such an interface.

An enum can be instantiated or is assignable via a static method Enum.valueOf(String). The valueOf() accepts a String instance and matches it to the declared enum variant.

Pattern matching for enum

The following pattern matching can be used in identifying enum instances.
Class.isEnum() – Works for enum without any body (no methods and extra attributes).
object instanceof Enum A regular instanceof check against java.lang.Enum.
Enum.class.isAssignableFrom(object.getClass()) – Using the Class.isAssignableFrom() matching.

Optional

A java.util.Optional allows two variants. The optional contains either a value of the specified generic or an empty.

Using Optional correctly, prevents the dreaded NullPointerException.

Additionally, an Optional guarantees that the consumer of the object will always receive an object and can act upon either the contained non-empty value, if present or handle the lack of value.

Read more about Optional at my Java Optional blog.

Pattern matching for Optional

The following pattern matching can be used in identifying Optional instances.
object instanceof Optional A regular instanceof check against java.util.Optional.
Optional.class.isAssignableFrom(object.getClass()) – Using the Class.isAssignableFrom() matching.

Sealed types

Java has had final classes and non-final (open, abstract) classes forever. These were two extremes in terms of inheritance. Limiting inheritance and extension to a finite set of was not very easy prior to the recent introduction of sealed types. Sealed types were introduced in Java 15.

A sealed type (class or interface) permits finite extensions or implementations while preventing any others not listed in the permits clause.

The permitted types can be either non-sealed or final (or can also be a record, more on this later in the blog). A non-sealed type implies it is open to extension.

Pattern matching for sealed types

The following pattern matching can be used in identifying Optional instances.
object instanceof <Class> A regular instanceof check against the class ancestry for the instance.
<Class>.class.isAssignableFrom(object.getClass()) – Using the Class.isAssignableFrom() matching.

Product Types

Class

A regular Java POJO (Plain Old Java Object) class is considered to be a product type. It is a composite which allows for attributes that are grouped together.

Pattern matching for POJO types

The following pattern matching can be used in identifying Optional instances.
object instanceof <Class> A regular instanceof check against the class ancestry for the instance.
<Class>.class.isAssignableFrom(object.getClass()) – Using the Class.isAssignableFrom() matching.

Tuple

A special POJO class extending what a Class offers. Tuples currently are not included in the Java, except maybe, the ephemeral Map.Entry (API) that is available while iterating over Map instances. Tuples can include Unit, Pair, Twin, Triple etc. Tuples are useful when using collections.

Pattern matching for Tuple types

The following pattern matching can be used in identifying Optional instances.
object instanceof <Class> A regular instanceof check against the class ancestry for the instance.
<Class>.class.isAssignableFrom(object.getClass()) – Using the Class.isAssignableFrom() matching.

Record

A record is an immutable data object introduced in Java 14.

A record can be declared with just its attributes. An all-attribute constructor and accessors (getters) for each attribute are synthetically generated. The accessors do not have a get prefix normally used in POJOs. The name of the attribute is the same as the name of the accessor for the attribute. Mutator (setter) methods are not allowed on a record.

A record also allows for a Compact Constructor. This constructor is exactly the same as an all-attribute constructor without have to list them in the constructor signature. A record can use the compact constructor to validate / enforce rules during instantiation.

A record can implement an interface. A record works well with sealed types. A sealed type can permit records.

Starting Java 15, a local record can be created within a method. This limits the scope of the record to within the said method.

Pattern matching for record types

The following pattern matching can be used in identifying Optional instances.
object.getClass().isRecord() Using the Class.isRecord().
object instanceof Record A regular instanceof check against java.lang.Record.
Record.class.isAssignableFrom(object.getClass()) – Using the Class.isAssignableFrom() matching.

Summary

We touched upon the few Algebraic Data Types in Java. There is a lot more to discuss. This includes Catamorphism, Homomorphism and Anamorphism. The next blog will include these. Additionally we will look into the Visitor Pattern, Variances (Invariance, Covariance and Contravariance).

Further, there is excellent reading material available online. Here are a few links to read more:

Thanks for reading !!!

#CommunityFIRST #SharingIsCaring #OpenSourceFUN

One thought on “Algebraic – in Java?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s