gosli: a little attempt to bring a bit of LINQ to Golang
Tags: go, linq, codegeneration, showdev
Published: 2020-04-17 21:02:05

---
title: gosli: a little attempt to bring a bit of LINQ to Golang
published: true
description:
tags: golang, linq, code generation, showdev
---

This post was originally posted on Medium: https://medium.com/@dmitrybogomolov/gosli-a-little-attempt-to-bring-a-bit-of-linq-to-golang-1e3fb4acfd04

![](https://dev-to-uploads.s3.amazonaws.com/i/fyabynksasw652zkdloa.png)

First, let me admit that I really like Golang. I think it’s pretty elegant, easy-to-read and very powerful language. But also, I love C#.

Though I know that every language has its’ best practices and has its’ own way to create good software, there’s something from C# that I definitely miss in Golang. One of those things is LINQ (Language-Integrated Query).

LINQ allows us to manipulate with collections in C#, modifying, filtering, grouping them using anonymous (lambda) functions. Let’s see a little example how it looks:

{% gist https://gist.github.com/doctornick42/c5af84da44a73d456368294c564c747f %}

##The problem

One of the biggest inconveniences I met in Go was the fact that the language doesn’t have some standard way to filter a slice or to find an element of slice by one of its’ fields. I tried to find some way to do it, but everything I found was something like “Well, you have to write a loop for it”.

Once upon a time, I found myself having multiple loops in my program, and they were almost the same but working with different types. It was something like that.

{% gist https://gist.github.com/doctornick42/509f67fbc3fd98dae74f40d72475dd04 %}

It made me think that it should be the way to avoid code copying. One approach is to make common method receiving `interface{}`.

{% gist https://gist.github.com/doctornick42/b96101972722034827e62567195af3c3 %}

And there are at least a couple of problems I see in this implementation.
First, a lot of type assertions are there (like `x.(B).FieldString…` etc.). It doesn’t look very nice and it makes us create a lot of code to check if assertion can be made.

Also, it makes us use the `reflect` package. A lot of Go apps are made to be fast, I mean, really fast. And you could hear it here and there that `reflect` is not a great helper if you’re looking for good performance.

And another disadvantage is the underlying type returned by our `filter` method. Let’s check it with `fmt.Printf(“%T”, filteredA)` and it will return us: `[]interface {}`. If we want to use this object it wouldn’t be to cool to do. We will need to iterate over it to convert every element’s type to `A`.

So, all these problems made me think that we shouldn’t make a pseudo-generic method for all the types. But writing a separated loop for every type (as we did in the first code example) doesn’t look too good as well.

I supposed that making a code generator to create some general methods that won’t use the `reflect` package inside of those methods could make a deal. That’s how we can avoid copying-and-pasting code all the time and also we could have nice and clear methods that receives and returns particular types instead of annoying `interface{}`.

##Introducing gosli

These thoughts lead me to make a lib I called `gosli`. I didn’t really plan to make a big deal out of it, I just wrote some code that, I guess, could be useful for someone else.

TL;DR: check the source code and examples of usage here 
https://github.com/doctornick42/gosli

Ok, let’s start. To get it we need to run this command:
`go get github.com/doctornick42/gosli`

Let’s create a new project with such a structure:

![Basic project structure](https://miro.medium.com/max/273/1*qdw5QRxirjx8cRCRxSqDVQ.png)

`testtypes.go` will contain our types from the previous examples:

{% gist https://gist.github.com/doctornick42/998405dfcd78416583c6277fab61bb5b %}

After it, we should run this for Linux

`$GOPATH/bin/gosli types/testtypes.go A`

or this for Windows:

`%GOPATH%\bin\gosli.exe types\testtypes.go A`

As you can see, our `types` folder has been extended by adding 3 new files:
![Generated files](https://miro.medium.com/max/272/1*M2g_51R4DRzPxiJ7-9XL5w.png)

The only file of them we need to modify manually is `a_equal.go`, which is needed for gosli to understand how it can check if two instances of `A` are equal. From the scratch the code of this file looks like:
{% gist https://gist.github.com/doctornick42/f234d2709bec936bcf001ed5ca195817 %}

Let’s append after the `// ``equal`` method has to be implemented manually` comment a new line:

`return r.FieldInt == another.FieldInt`

That’s it. Now we can make some code to test the result, let’s just use the code we used in a previous example and put it in our `main.go` file:

{% gist https://gist.github.com/doctornick42/8ec9cb30dc503cae945d80e7bbb072ef %}

And if we run the program we will see this output: `[{“FieldInt”:1},{“FieldInt”:3}]` which is correct.

The full code of this example [is here](https://github.com/doctornick42/gosli-example).

##Features of gosli

The library has 2 main approaches:
* to provide methods for slice of custom types. This part uses code generation, it has been briefly described in the previous paragraph. The documentation showing all the methods and other details could be [found here](https://github.com/doctornick42/gosli/blob/master/docs/custom.md);
* gosli also has all these methods for basic Golang types out-of-the box, it’s [described here](https://github.com/doctornick42/gosli/blob/master/docs/primitives.md).


Also, every method of -Slice types returns such a type too, so the methods could be combined in a chain like this:
```Go
result, err := SomeTypeSlice(original).
    Where(filter1).
    Where(filter2).
    Page(1, 2)
```

So, thank you for reading. If you would like to check out gosli, feel free to ask me about it or create issues in the repository.

In the [experiment](https://github.com/doctornick42/gosli/tree/master/experiment) folder there are some tests, benchmarks and examples, that I hope will help to understand how to use the library in your apps.

Also, you can find more awesome gopher images (like one I used in this article) by Egon Elbre under the [CC0 license](https://creativecommons.org/publicdomain/zero/1.0/) here https://github.com/egonelbre/gophers