F# Inheritance: Unlocking the Power of Code Reuse

In this article, you will learn how to implement F# inheritance using classes, methods, and properties. You will also discover how to override or create new properties and methods in F# classes. Additionally, we will delve into the topic of abstract classes and how they can be implemented using inheritance in F#.

If you’re familiar with object-oriented programming, you know that the idea of inheritance is a core concept that allows you to build new classes based on existing ones. The good news is that F#, just like any other object-oriented programming language, has powerful inheritance capabilities that enable you to inherit existing classes or create new ones by inheriting attributes from those classes. This means you can reuse existing code and modify it to add new functionality, making your programming process much simpler.

In F#, you can designate a new class to inherit the data members and functions of an existing class, rather than writing all new data members and functions from scratch. The existing class is referred to as the base class, while the new class is referred to as the derived class. This allows you to establish a relationship between the base class and the derived class, where the derived class inherits properties and behavior from the base class.



Sub Classes And Base Classes

In F#, you can define subclasses as descendants of base classes that are already defined.

Subclasses inherit members (properties, methods, and fields) of their base classes and can also have their own members.

This allows you to reuse existing code from the base class and extend or customize the behavior in the subclass.

To define a subclass in F#, you can use the inherit keyword.

Here’s an example:

type MyDerived(...) 
   inherit MyBase(...)
Important: Classes in F# can have a maximum of one direct base class. Class implicitly inherits from Object if the inherit keyword is not used to specify a base class.
A user of the derived class has access to the methods and members of the base class as they would with members of the derived class directly.
It is important to note that let bindings and constructor parameters are private to the class, so derived classes cannot access them.

Whenever the keyword base is used, it refers to the instance of the base class. It acts as a self-identifier.

Example: 

type User(name) = member x.Name = name member x.Greet() = printfn "Hey, I'm %s" x.Nametype Programmer(name, expertise : string) = inherit User(name)let mutable _salary = 20000.0 member x.Salary with get() = _salary and set value = _salary <- value member x.Expertise = expertisetype Tester(name, expertise : string) = inherit User(name)let mutable _salary = 16999.0 member x.Salary with get() = _salary and set value = _salary <- value member x.Expertise = expertise //using the subclasses let p = new User("Alice") let prog = new Programmer("Ben", "Java") let test = new Tester("John", "Selenium")p.Greet() prog.Greet() printfn "With expertise in %s with %.2f salary" prog.Expertise prog.Salary test.Greet() printfn "With expertise in %s with %.2f salary" test.Expertise test.Salary

Upon execution of above code you will see below output:

Hey, I'm Alice
Hey, I'm Ben
With expertise in Java with 20000.00 salary
Hey, I'm John
With expertise in Selenium with 16999.00 salar

F# Inheritance Overriding Methods

As a developer, you can override the default behavior of the method on the base class in order to implement the method differently in the subclass or the derived class.

The default behavior in F# is that methods cannot be overridden.

You must declare the method you wish to override using the abstract and default keywords in your derived class, in order to override the method in the derived class:

type User(name) =
member x.Name = name
abstract Greet : unit -> unit
default x.Greet() = printfn "Hey, I’m %s" x.Name

Now, derived classes can override the Greet method of the Person class by creating their own Greet method.

This can be demonstrated in the following example:

Example: 

type User(name) = member x.Name = name abstract Greet : unit -> unit default x.Greet() = printfn "Hey, I'm %s" x.Nametype Programmer(name, expertise : string) = inherit User(name)let mutable _salary = 20000.0 member x.Salary with get() = _salary and set value = _salary <- value member x.Expertise = expertise override x.Greet() = printfn "Programmer %s." x.Nametype Tester(name, expertise : string) = inherit User(name)let mutable _salary = 16999.0 member x.Salary with get() = _salary and set value = _salary <- value member x.Expertise = expertise override x.Greet() = printfn "Tester %s." x.Name //using the subclasses let p = new User("Alice") let prog = new Programmer("Ben", "Java") let test = new Tester("John", "Selenium")p.Greet() prog.Greet() printfn "With expertise in %s with %.2f salary" prog.Expertise prog.Salary test.Greet() printfn "With expertise in %s with %.2f salary" test.Expertise test.Salary

And it outputs the following result:

Hey, I'm Alice
Programmer Ben.
With expertise in Java with 20000.00 salary
Tester John.
With expertise in Selenium with 16999.00 salary

Abstract Class

Sometimes it is necessary to provide an incomplete implementation of an object, something that was not meant to be implemented in real life. It is likely that another programmer will create subclasses of the abstract class in the future, in order to complete implementation of the abstract class.

For example, an Office management system does not need a User class, if it is designed to manage users. It is still necessary to use the Programmer class or the Tester class in this case. It is possible to declare a user class as an abstract class in such cases.

An abstract member of a class can be identified through the AbstractClass attribute.

An abstract class cannot be created as an instance because it has not been fully implemented.

Here is an example of how this can be demonstrated:

Example: 

[<AbstractClass>] type User(name) = member x.Name = name abstract Greet : unit -> unit default x.Greet() = printfn "Hey, I'm %s" x.Nametype Programmer(name, expertise : string) = inherit User(name)let mutable _salary = 20000.0 member x.Salary with get() = _salary and set value = _salary <- value member x.Expertise = expertise override x.Greet() = printfn "Programmer %s." x.Nametype Tester(name, expertise : string) = inherit User(name)let mutable _salary = 16999.0 member x.Salary with get() = _salary and set value = _salary <- value member x.Expertise = expertise override x.Greet() = printfn "Tester %s." x.Name //using the subclasses let prog = new Programmer("Ben", "Java") let test = new Tester("John", "Selenium")prog.Greet() printfn "With expertise in %s with %.2f salary" prog.Expertise prog.Salary test.Greet() printfn "With expertise in %s with %.2f salary" test.Expertise test.Salary

Output:

Programmer Ben.
With expertise in Java with 20000.00 salary
Tester John.
With expertise in Selenium with 16999.00 salary

Example Explanation

We have an abstract class named User, which has a constructor that takes a name parameter. The class has a property called Name which returns the name passed in the constructor. It also has an abstract method called Greet.

We then have two subclasses named Programmer and Tester. Both inherit from the User class and take a name and expertise parameter into their constructors. There is a Salary property and an Expertise property that return the salaries and expertise passed in each constructor.

Both subclasses also override the Greet method of the User class, and instead of just printing “Hey, I’m {name}”, they print “Programmer {name}” and “Tester {name}” respectively.

We create instances of both subclasses, assign them to variables named prog and test, and then call their Greet methods. We also print out their Expertise and Salary properties using printfn statements.

In summary, we have an abstract class and two subclasses that inherit from it. The subclasses have their own properties and methods, and we can create instances of them and use their properties and methods.


Where to Use F# Inheritance?

F# inheritance is a powerful feature that you can utilize in various scenarios to achieve efficient code organization, reuse, and extensibility.

As a programmer, you can leverage F# inheritance in the following ways:

Creating Hierarchies

You can use F# inheritance to create class hierarchies where multiple classes share common functionality.

For example, in a game development scenario, you can have a base class for a generic character and then derive classes for specific character types, such as player characters, NPCs, and enemies, inheriting common properties and methods from the base class.

Extending Existing Classes

You can also use F# inheritance to extend the functionality of existing classes without modifying their implementation.

You can create a derived class that inherits from the base class and adds or overrides properties, methods, or other members to customize the behavior of the base class according to your specific requirements.

Implementing Interfaces

F# supports interface-based programming, and you can use inheritance to implement interfaces in classes.

You can create a class that inherits from a base class and implements one or more interfaces, providing the implementation for the required members of the interface.

Encapsulating Common Functionality

You can use F# inheritance to encapsulate common functionality in a base class, allowing derived classes to inherit and reuse this functionality.

This can help in promoting code organization and reducing code duplication, making your code more maintainable and scalable.

Implementing Design Patterns

You can use F# inheritance to implement common design patterns such as the Template Method pattern, where a base class defines a common template for a sequence of steps and derived classes provide concrete implementations for specific steps.

Inheritance can also be used in other design patterns like the Strategy pattern, Decorator pattern, and Factory pattern, among others.


Conclusion

F# Inheritance allows you to reuse and extend existing code, create specialized classes, and enforce common behavior through abstract classes. By leveraging inheritance, you can write more efficient and maintainable code, and easily customize the behavior of your classes to suit your specific needs. With the concepts and examples discussed in this article, you are well-equipped to unlock the full potential of F# inheritance and take your object-oriented programming skills to the next level. Happy coding!

We value your feedback.
+1
0
+1
0
+1
0
+1
0
+1
0
+1
0
+1
0

Subscribe To Our Newsletter
Enter your email to receive a weekly round-up of our best posts. Learn more!
icon

Leave a Reply

Your email address will not be published. Required fields are marked *