DEV Community

Siva
Siva

Posted on • Originally published at Medium on

Introduction to Generic Programming in Go: Filtering and Summing Numeric Types

Go is a statically typed language with a strict type system, which means that each variable and function parameter must have a specific type defined at compile-time. While this makes Go programs fast and efficient, it can also make them less flexible and harder to write. To address this issue, Go 1.18 introduced support for generics, allowing programmers to write code that can work with a wide range of types.

In this tutorial , we’ll explore how to use generics to implement a filter function that works with numeric types. Our filter function will take a list of values and a predicate function that returns true or false based on whether a value should be included in the filtered list. Additionally, our filter function will return the sum of the filtered values.

Step 1: Defining the Numeric Interface

To implement our filter function, we’ll define a Numeric interface that all numeric types must satisfy. The Numeric interface will have two methods: Add and IsZero. The Add method will take another value and return the sum of the two values, while the IsZero method will return true if the value is zero.

type Numeric interface {
    Add(x interface{}) Numeric
    IsZero() bool
}
Enter fullscreen mode Exit fullscreen mode

We’ll use the interface{} type to define the parameter type of the Add method, which will allow us to pass in any type of value to the method.

Step 2: Implementing Numeric Types

Next, we’ll implement two numeric types that satisfy the Numeric interface: Int and Float. The Int type will be an integer type that implements the Add and IsZero methods, while the Float type will be a floating-point type that also implements the Add and IsZero methods.

type Int int

func (i Int) Add(x interface{}) Numeric {
    switch x := x.(type) {
    case Int:
        return i + x
    }
    return nil
}

func (i Int) IsZero() bool {
    return i == 0
}

type Float float64

func (f Float) Add(x interface{}) Numeric {
    switch x := x.(type) {
    case Float:
        return f + x
    }
    return nil
}

func (f Float) IsZero() bool {
    return f == 0.0
}
Enter fullscreen mode Exit fullscreen mode

The Add method for both types uses a type switch to determine the type of the parameter and returns the sum of the two values if the parameter is of the same type as the receiver. Otherwise, it returns nil.

Step 3: Implementing the Filter Function

Now that we’ve defined the Numeric interface and implemented the Int and Float types, we can use them to implement our filter function. The Filter function takes a list of values and a predicate function as input and returns a filtered list and the sum of the filtered values.

func Filter[T Numeric](list []T, predicate func(T) bool) ([]T, T) {
    var result []T
    var sum T
    for _, item := range list {
        if predicate(item) {
            result = append(result, item)
            if sum.IsZero() {
                sum = item
            } else {
                sum = sum.Add(item).(T)
            }
        }
    }
    return result, sum
}
Enter fullscreen mode Exit fullscreen mode

The Filter function uses a type parameter T, which is constrained by the Numeric interface, to specify the type of values in the list parameter. The predicate function takes a single value of type T as input and returns true or false.

Inside the function, we initialize two variables: result, which is an empty slice of T, and sum, which is initialized to the zero value of type T. We then loop through each item in the list and check whether the predicate function returns true for that item.

If the predicate function returns true, we append the item to the result slice and add it to the current value of sum using the Add method defined in the Numeric interface.

We also use the IsZero method to check whether the current value of sum is the initial zero value of type T or has been updated with a non-zero value. Finally, we return the filtered result slice and the total sum of the filtered items. This implementation allows us to use the Filter function with any type that implements the Numeric interface, providing a flexible and reusable solution for filtering and summing elements of a list.

Extra’s

A Simpler Explanation

So imagine you have a bunch of numbers and you want to find which ones are even (divisible by 2) and also find the total sum of those even numbers.

In our program, we have created a function called Filter that takes in a list of numbers and a condition to filter those numbers. The Filter function returns two things: the filtered list of numbers that meet the condition, and the sum of those filtered numbers.

We have also created a type called Numeric that represents any numeric data type like int, float, etc. This type has two methods: Add and IsZero. The Add method takes in another Numeric value and returns the sum of the two values. The IsZero method checks if the value is equal to zero.

Now let’s go back to the Filter function. Inside the function, we initialize two variables: result which is an empty list of the same type as the input list, and sum which is initialized to the zero value of the same type as the input list. We then loop through each item in the input list and check whether the item meets the condition specified by the predicate function. If it does, we add it to our result list and also add it to our sum variable using the Add method.

At the end of the loop, we return the result list and the sum variable, which gives us the filtered list and the sum of the filtered numbers respectively.

DEMO — Code Walkthrough

Top comments (0)