Во-первых, позвольте мне признать, что мне очень нравится Голанг. Я считаю, что это довольно элегантный, легкий для чтения и очень мощный язык. Но также я люблю C #.

Хотя я знаю, что у каждого языка есть свои лучшие практики и свой собственный способ создания хорошего программного обеспечения, в C # есть кое-что, чего мне определенно не хватает в Golang. Одна из таких вещей - LINQ (Language-Integrated Query).

LINQ позволяет нам манипулировать коллекциями на C #, изменять, фильтровать, группировать их с помощью анонимных (лямбда) функций. Давайте посмотрим на небольшом примере, как это выглядит:

Эта проблема

Одним из самых больших неудобств, с которыми я столкнулся в Go, было то, что в языке нет стандартного способа фильтрации фрагмента или поиска элемента фрагмента по одному из его полей. Я пытался найти способ сделать это, но все, что я нашел, было что-то вроде «Ну, вы должны написать для этого цикл».

Однажды я обнаружил, что в моей программе было несколько циклов, и они были почти одинаковыми, но работали с разными типами. Было что-то в этом роде.

Это заставило меня подумать, что это должен быть способ избежать копирования кода. Один из подходов - сделать общий метод получения interface{}.

И в этой реализации я вижу по крайней мере пару проблем.

Во-первых, существует множество утверждений типа (например, x.(B).FieldString… и т. Д.). Это выглядит не очень красиво и заставляет нас создавать много кода, чтобы проверить, можно ли сделать утверждение.

Кроме того, это заставляет нас использовать пакет reflect. Многие приложения Go созданы, чтобы быть быстрыми, я имею в виду, очень быстрыми. Кое-где можно было слышать, что reflect - не лучший помощник, если вы ищете хорошую производительность.

И еще один недостаток - это базовый тип, возвращаемый нашим filter методом. Давайте проверим его с fmt.Printf(“%T”, filteredA), и он вернет нам: []interface {}. Если мы хотим использовать этот объект, это было бы не круто. Нам нужно будет перебрать его, чтобы преобразовать каждый тип элемента в A.

Итак, все эти проблемы заставили меня подумать, что мы не должны создавать псевдо-общий метод для всех типов. Но написание отдельного цикла для каждого типа (как в первом примере кода) тоже выглядит не очень хорошо.

Я предположил, что создание генератора кода для создания некоторых общих методов, которые не будут использовать пакет reflect внутри этих методов, может помочь. Вот как мы можем избежать постоянного копирования и вставки кода, а также иметь красивые и понятные методы, которые принимают и возвращают определенные типы вместо того, чтобы раздражать interface{}.

Представляем госли

Эти мысли привели меня к созданию библиотеки, которую я назвал gosli. На самом деле я не планировал придавать этому большого значения, я просто написал код, который, как мне кажется, может быть полезен кому-то другому.

TL; DR: проверьте исходный код и примеры использования здесь https://github.com/doctornick42/gosli

Хорошо, давайте начнем. Чтобы его получить, нам нужно запустить эту команду:

go get github.com/doctornick42/gosli

Создадим новый проект с такой структурой:

testtypes.go будет содержать наши типы из предыдущих примеров:

После этого мы должны запустить это для Linux

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

или это для Windows:

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

Как видите, наша папка types была расширена за счет добавления 3 новых файлов:

Единственный из них, который нам нужно изменить вручную, - это a_equal.go, который необходим gosli, чтобы понять, как он может проверить, равны ли два экземпляра A. С нуля код этого файла выглядит так:

Добавим после // `equal` method has to be implemented manually комментария новую строку:

return r.FieldInt == another.FieldInt

Вот и все. Теперь мы можем написать код для проверки результата, давайте просто воспользуемся кодом, который мы использовали в предыдущем примере, и поместим его в наш main.go файл:

И если мы запустим программу, мы увидим такой результат: [{“FieldInt”:1},{“FieldInt”:3}], что правильно.

Полный код этого примера здесь.

Особенности госли

У библиотеки есть 2 основных подхода:

  • чтобы предоставить методы для фрагментов настраиваемых типов. В этой части используется генерация кода, она была кратко описана в предыдущем абзаце. Документацию, показывающую все методы и другие детали, можно найти здесь;
  • В gosli также есть все эти методы для базовых типов Golang из коробки, они описаны здесь.

Кроме того, каждый метод типов -Slice также возвращает такой тип, поэтому методы можно объединить в такую ​​цепочку:

result, err := SomeTypeSlice(original).
    Where(filter1).
    Where(filter2).
    Page(1, 2)

Итак, спасибо за чтение. Если вы хотите проверить gosli, не стесняйтесь спрашивать меня об этом или создавать проблемы в репозитории.

В папке эксперимент есть несколько тестов, тестов и примеров, которые, я надеюсь, помогут понять, как использовать библиотеку в ваших приложениях.

Кроме того, вы можете найти другие потрясающие изображения сусликов (например, те, которые я использовал в этой статье) от Эгона Эльбра под лицензией CC0 здесь https://github.com/egonelbre/gophers