Привет!👋🏾
Давай знакомиться!
Меня зовут Крис. Мое хобби и страсть — это программирование, погружение в операционные системы и всё, что связано с новыми технологиями.
Я не буду твоим учителем или наставником. Я скорее попутчик. Пока я пишу посты, я сам изучаю новые темы. Делясь этим знанием с тобой, я его структурирую и закрепляю. Если я что-то понял — делюсь, если накосячил — тоже расскажу. Так что наше общение — это mutual journey.
«Что за странное название?» — спросишь ты.
Сейчас я глубоко погрузился в изучение Haskell, и это название показалось мне идеальным. Оно отсылает к объявлению функции this, которая возвращает так называемый побочный эффект с типом Diary. Для меня это значит, что весь этот канал — и есть «побочный эффект» моего обучения. Я хочу делиться процессом и знаниями с внешним миром.
Чем я буду делиться здесь?
— 📊 Функциональное программирование (Haskell, теория, матчасть)
— 🐧 Кастомизация Linux (райсинг, удобный workflow)
— ⚙️ Написание скриптов (автоматизация всего)
— И всё, что хоть как-то связано с Linux и разработкой.
Очень рад, что ты здесь! Добро пожаловать в мой цифровой дневник. 😊
Давай знакомиться!
Меня зовут Крис. Мое хобби и страсть — это программирование, погружение в операционные системы и всё, что связано с новыми технологиями.
Я не буду твоим учителем или наставником. Я скорее попутчик. Пока я пишу посты, я сам изучаю новые темы. Делясь этим знанием с тобой, я его структурирую и закрепляю. Если я что-то понял — делюсь, если накосячил — тоже расскажу. Так что наше общение — это mutual journey.
«Что за странное название?» — спросишь ты.
Сейчас я глубоко погрузился в изучение Haskell, и это название показалось мне идеальным. Оно отсылает к объявлению функции this, которая возвращает так называемый побочный эффект с типом Diary. Для меня это значит, что весь этот канал — и есть «побочный эффект» моего обучения. Я хочу делиться процессом и знаниями с внешним миром.
Чем я буду делиться здесь?
— 📊 Функциональное программирование (Haskell, теория, матчасть)
— 🐧 Кастомизация Linux (райсинг, удобный workflow)
— ⚙️ Написание скриптов (автоматизация всего)
— И всё, что хоть как-то связано с Linux и разработкой.
Очень рад, что ты здесь! Добро пожаловать в мой цифровой дневник. 😊
❤9🍌1
❓Что такое функциональное программирование?
Перед объяснением концепций функционального программирования вроде каррирования и ленивых вычислений, стоит объяснить, что вообще такое функциональное программирование. Все много раз слышали о такой парадигме, но уверен, что немногие по-настоящему понимают, в чем она заключается.
Функциональное программирование (далее ФП) — подход архитектурного дизайна, основанный на использовании функций в математическом их понимании. Да, это не про использование функций
Чистые функции
Это фундаментальный строительный блок ФП. Чистая функция — это аналог математической функции, где выход зависит только от входных аргументов, и у нее нет никаких побочных эффектов. Она обладает следующими свойствами:
— Детерминированность: Для одних и тех же входных данных всегда возвращает один и тот же результат.
— Идемпотентность: Повторное выполнение процесса не меняет состояние системы (не модифицирует глобальные переменные, не пишет в файл, не делает сетевых запросов), не мутирует свои аргументы.
Иммутабельность
Данные не изменяются. Вместо того чтобы мутировать существующую структуру (например, изменить элемент массива), создается ее новая копия с желаемыми изменениями.
Функции высшего порядка
Функции, которые могут принимать другие функции в качестве аргументов и/или возвращать функции как результат. Это мощный инструмент для абстракции и композиции поведения.
Декларативность
ФП — это декларативный подход. Код описывает что нужно сделать (какое преобразование применить к данным), а не как это сделать шаг за шагом (как в императивном стиле с циклами и переменными-счетчиками). Цепочка
Есть ещё пара принципов, но они уже не влезут в пост, да и эти являются главными. Советую прочитать пост моего коллеги, там он наглядно продемонстрировал эти принципы на языке
#functional #theory
Перед объяснением концепций функционального программирования вроде каррирования и ленивых вычислений, стоит объяснить, что вообще такое функциональное программирование. Все много раз слышали о такой парадигме, но уверен, что немногие по-настоящему понимают, в чем она заключается.
Функциональное программирование (далее ФП) — подход архитектурного дизайна, основанный на использовании функций в математическом их понимании. Да, это не про использование функций
map и filter, стрелочных функций и рекурсии, как считают многие. Это про мышление функциями в их строгом математическом смысле. В основе этого мышления лежат несколько ключевых принципов, которые отличают ФП от императивных стилей программирования:Чистые функции
Это фундаментальный строительный блок ФП. Чистая функция — это аналог математической функции, где выход зависит только от входных аргументов, и у нее нет никаких побочных эффектов. Она обладает следующими свойствами:
— Детерминированность: Для одних и тех же входных данных всегда возвращает один и тот же результат.
— Идемпотентность: Повторное выполнение процесса не меняет состояние системы (не модифицирует глобальные переменные, не пишет в файл, не делает сетевых запросов), не мутирует свои аргументы.
Иммутабельность
Данные не изменяются. Вместо того чтобы мутировать существующую структуру (например, изменить элемент массива), создается ее новая копия с желаемыми изменениями.
Функции высшего порядка
Функции, которые могут принимать другие функции в качестве аргументов и/или возвращать функции как результат. Это мощный инструмент для абстракции и композиции поведения.
map, filter и reduce — классические примеры функций высшего порядка, но они являются лишь следствием парадигмы, а не ее сутью.Декларативность
ФП — это декларативный подход. Код описывает что нужно сделать (какое преобразование применить к данным), а не как это сделать шаг за шагом (как в императивном стиле с циклами и переменными-счетчиками). Цепочка
map и filter читается как спецификация результата, а не инструкция по его получению.Есть ещё пара принципов, но они уже не влезут в пост, да и эти являются главными. Советую прочитать пост моего коллеги, там он наглядно продемонстрировал эти принципы на языке
Typescript. Если есть вопросы или хочется что-то добавить — добро пожаловать в комментарии!#functional #theory
Telegram
Душный инженер
🧮 Функциональное программирование: разбираем основы
ФП — это НЕ просто написание кода через функции, как и ООП — не только классы.
Главная идея ФП: функция — это не набор действий, а результат вычислений (как в математике).
4 основных концепции:
• Декларативность…
ФП — это НЕ просто написание кода через функции, как и ООП — не только классы.
Главная идея ФП: функция — это не набор действий, а результат вычислений (как в математике).
4 основных концепции:
• Декларативность…
❤4
🏗 Каррирование
На самом деле, функция имеет всегда один параметр. Даже, к примеру, функция
Для дальнейшего повествования мне нужно кратко ввести тебя в определение функций в Haskell. Объявление функции из примера выглядит следующим образом:
Последний
А что, если я хочу сделать функцию, которая применяет данную функцию к какому-то значению? Сделаем мы это так:
Не пугайся этих
Этих вводных будет достаточно, чтобы разбираться с поставленным вопросом. Что такое каррирование, зачем оно нужно, и почему функции в Haskell принимают только один параметр? Перед объяснением продемонстрирую каррирование во всей красе:
А так вообще можно? Можно, и очень часто нужно. Советую перечитать этот пример несколько раз, прежде чем переходить к определению, так как оно может взорвать и без того кипящую голову.
Каррирование (currying) — это преобразование функции от нескольких аргументов в последовательность функций, каждая из которых принимает только один аргумент и возвращает новую функцию для приёма следующего. Представь, что вместо того чтобы дать тебе весь бутерброд сразу, я даю сначала хлеб, затем ты просишь масло, затем — колбасу. После каждого шага ты получаешь промежуточный результат (ломтик хлеба, намазанный маслом), к которому можно что-то добавить.
Применяя это определение к примеру выше, делаем вывод, что выражение
То есть, этот пример мы можем переписать следующим образом. Результат никак не изменится:
В контексте Typescript это выглядит так:
Зачем это нужно? Для частичного применения функций. С помощью этого метода можно очень удобно делать полезные функции и упрощать лямбда-выражения. Пример:
Получился очень запутанный пост, и без должной подготовки понять суть каррирования очень сложно. Однако это одно из фундаментальных понятий ФП, используемое повсеместно.
#functional #theory #haskell
На самом деле, функция имеет всегда один параметр. Даже, к примеру, функция
add 3 10, которая принимает, как кажется, два параметра и возвращает результат сложения. Не веришь? Я тоже не верил🥴Для дальнейшего повествования мне нужно кратко ввести тебя в определение функций в Haskell. Объявление функции из примера выглядит следующим образом:
add :: Integer -> Integer -> Integer
add a b = a + b
Последний
Integer — тип возвращаемого значения. Первый и второй — типы входных параметров a и b соответственно. А что, если я хочу сделать функцию, которая применяет данную функцию к какому-то значению? Сделаем мы это так:
apply :: (a -> b) -> a -> b
apply f x = f x
apply (\x -> x * 2) 5 -- Результат: 10
Не пугайся этих
a и b в определении. Это псевдонимы типов, что-то вроде дженериков. В данном примере выражение в скобках (a -> b) — сигнатура функции, принимающей любой тип a и возвращающий любой тип b (не обязательно отличный от первого). Второй параметр — то самое значение, к которому мы будем применять функцию — имеет тип a. Последняя b — тип возвращаемого значения функции apply. Согласен, выглядит максимально запутанно, поэтому я немного тебя распутаю. Вот эквивалентная запись в Typescript, для понимания, что здесь вообще происходит:function apply<T, V>(callback: (x: T) => V, value: T): V {
return callback(value)
}
apply<Number, Number>((x) => x * 2, 5) // Результат: 10Этих вводных будет достаточно, чтобы разбираться с поставленным вопросом. Что такое каррирование, зачем оно нужно, и почему функции в Haskell принимают только один параметр? Перед объяснением продемонстрирую каррирование во всей красе:
add :: Integer -> Integer -> Integer
add x y = x + y
let addTwo = add 2
addTwo 3 -- Результат: 5
А так вообще можно? Можно, и очень часто нужно. Советую перечитать этот пример несколько раз, прежде чем переходить к определению, так как оно может взорвать и без того кипящую голову.
Каррирование (currying) — это преобразование функции от нескольких аргументов в последовательность функций, каждая из которых принимает только один аргумент и возвращает новую функцию для приёма следующего. Представь, что вместо того чтобы дать тебе весь бутерброд сразу, я даю сначала хлеб, затем ты просишь масло, затем — колбасу. После каждого шага ты получаешь промежуточный результат (ломтик хлеба, намазанный маслом), к которому можно что-то добавить.
Применяя это определение к примеру выше, делаем вывод, что выражение
add 2 вполне валидно и возвращает новую функцию, принимающую ещё один параметр — к нему мы прибавим двойку. Для тех, кто в теме — это очень похоже на замыкание (closure): внутри функции мы заключаем какое-то состояние в качестве параметра, с которым сможем работать позднее.То есть, этот пример мы можем переписать следующим образом. Результат никак не изменится:
add :: Integer -> (Integer -> Integer)
add x y = x + y
let addTwo = add 2
addTwo 3 -- Результат: 5
В контексте Typescript это выглядит так:
function add(x) {
return (y) => x + y
}
const addTwo = add(2)
addTwo(3) // Результат: 5Зачем это нужно? Для частичного применения функций. С помощью этого метода можно очень удобно делать полезные функции и упрощать лямбда-выражения. Пример:
let list = [1, 2, 3, 4]
-- Эти три записи эквивалентны
let incrementList a = map (\x -> x + 1) a
let incrementList' a = map (1 +) a
let incrementList'' = map (1 +)
--
let getPositives = filter (> 0)
getPositives [-2, 5, 3, 0 -9] -- Результат: [5, 3]
Получился очень запутанный пост, и без должной подготовки понять суть каррирования очень сложно. Однако это одно из фундаментальных понятий ФП, используемое повсеместно.
#functional #theory #haskell
🔥3
😴 Lazy Evaluation
Любой программист хочет сделать максимально эффективную с точки зрения памяти и производительности программу. Андреас Румпф, разработчик языка Nim, специально для этого разработал несколько механизмов сборки мусора, хотя в то время пользователи очень нуждались в фичах. А можно ли как-то обойтись без шаманских ритуалов со сборщиками мусора, механизмами компиляции и прочего, посмотреть на оптимизацию с другой стороны? Можно – придумать такой механизм, который пропускает инструкции, не требующие сиюминутного выполнения.
Мой репетитор по физике был очень ленивым человеком: "Я подставлю значения в эту формулу в самый последний момент, когда переменных будет совсем немного". В действительности это экономит очень много времени и сил – сложность вычислений сильно снижается, так как часть переменных просто сокращается. Если с самого начала оперировать цифрами, выполняется слишком много лишней работы. Именно это легло в основу функциональной парадигмы.
Ленивые вычисления – это фундаментальная стратегия выполнения кода, при которой любое выражение вычисляется только в тот момент, когда оно действительно нужно. Сразу перейдем к примерам:
Сколько памяти эта программа потребует для выполнения? Сколько времени понадобится на аллокацию памяти, чтобы в итоге вывести 5 первых элементов? Много.
В императивных языках преобладают строгие (энергичные) вычисления, являющиеся противоположностью ленивых – выполняем все именно так, как и написано, даже если результат не потребуется. Поэтому сначала создастся массив на 2³² элементов, а потом выведутся первые 5 элементов.
Тот же пример на Haskell:
А сколько здесь памяти потребуется? Столько, сколько нужно для 5 чисел типа
Зачем это нужно?
1. Работа с бесконечными структурами данных. Это визитная карточка ленивости. Ты можешь описать бесконечную последовательность (все простые числа, поток данных от датчика, числа Фибоначчи) и использовать её конечную часть, не опасаясь зависания:
2. Повышение модульности и переиспользования кода. Ты можешь разделить код на независимые части: генератор данных (который просто описывает, что можно создать) и потребитель (который решает, сколько ему нужно). Ленивость эффективно связывает их воедино без написания сложных циклов и условий.
3. Экономия ресурсов. Позволяет избежать ненужных вычислений и создания промежуточных массивов данных, что критично для обработки больших данных и высокопроизводительных приложений.
Но не может ведь быть все настолько радужно. Где-то должен быть подвох.
Обратная сторона
Ленивость усложняет предсказание производительности. Поскольку вычисления происходят не там, где они записаны, а в том месте, где потребовался их результат, бывает сложно отладить проблемы с памятью. Накопление невычисленных обещаний (thunks) может привести к ее избыточному потреблению.
Итог: Lazy evaluation позволяет писать более декларативный, выразительный и зачастую более эффективный код при умелом использовании. Это краеугольный камень многих функциональных языков и полезный инструмент, который проник даже в строгие императивные языки (yield и генераторы), помогая разработчикам оперировать данными более гибко.
#functional #theory #haskell
Любой программист хочет сделать максимально эффективную с точки зрения памяти и производительности программу. Андреас Румпф, разработчик языка Nim, специально для этого разработал несколько механизмов сборки мусора, хотя в то время пользователи очень нуждались в фичах. А можно ли как-то обойтись без шаманских ритуалов со сборщиками мусора, механизмами компиляции и прочего, посмотреть на оптимизацию с другой стороны? Можно – придумать такой механизм, который пропускает инструкции, не требующие сиюминутного выполнения.
Мой репетитор по физике был очень ленивым человеком: "Я подставлю значения в эту формулу в самый последний момент, когда переменных будет совсем немного". В действительности это экономит очень много времени и сил – сложность вычислений сильно снижается, так как часть переменных просто сокращается. Если с самого начала оперировать цифрами, выполняется слишком много лишней работы. Именно это легло в основу функциональной парадигмы.
Ленивые вычисления – это фундаментальная стратегия выполнения кода, при которой любое выражение вычисляется только в тот момент, когда оно действительно нужно. Сразу перейдем к примерам:
arr = list(range(0, 2**32))
print(arr[0:5])
Сколько памяти эта программа потребует для выполнения? Сколько времени понадобится на аллокацию памяти, чтобы в итоге вывести 5 первых элементов? Много.
В императивных языках преобладают строгие (энергичные) вычисления, являющиеся противоположностью ленивых – выполняем все именно так, как и написано, даже если результат не потребуется. Поэтому сначала создастся массив на 2³² элементов, а потом выведутся первые 5 элементов.
Тот же пример на Haskell:
take 5 [0..2^32]
А сколько здесь памяти потребуется? Столько, сколько нужно для 5 чисел типа
Integer. Определение массива [0..2^32] – это, на самом деле, не определение, а обещание: "Да, я сделаю тебе массив из 2³² элементов, но только тогда, когда тебе это будет нужно". А функция take – это функция, которая требует результат этого обещания. Он обращается к несуществующему массиву и получает 5 элементов один за одним. Память и процессорное время тратятся только на то, что пошло в финальный результат.Зачем это нужно?
1. Работа с бесконечными структурами данных. Это визитная карточка ленивости. Ты можешь описать бесконечную последовательность (все простые числа, поток данных от датчика, числа Фибоначчи) и использовать её конечную часть, не опасаясь зависания:
-- Бесконечный список всех натуральных чисел
numbers = [1, 2, 3, ..]
-- Взять от него ровно 10 элементов — и остальные не будут созданы
take 10 numbers
2. Повышение модульности и переиспользования кода. Ты можешь разделить код на независимые части: генератор данных (который просто описывает, что можно создать) и потребитель (который решает, сколько ему нужно). Ленивость эффективно связывает их воедино без написания сложных циклов и условий.
3. Экономия ресурсов. Позволяет избежать ненужных вычислений и создания промежуточных массивов данных, что критично для обработки больших данных и высокопроизводительных приложений.
Но не может ведь быть все настолько радужно. Где-то должен быть подвох.
Обратная сторона
Ленивость усложняет предсказание производительности. Поскольку вычисления происходят не там, где они записаны, а в том месте, где потребовался их результат, бывает сложно отладить проблемы с памятью. Накопление невычисленных обещаний (thunks) может привести к ее избыточному потреблению.
Итог: Lazy evaluation позволяет писать более декларативный, выразительный и зачастую более эффективный код при умелом использовании. Это краеугольный камень многих функциональных языков и полезный инструмент, который проник даже в строгие императивные языки (yield и генераторы), помогая разработчикам оперировать данными более гибко.
#functional #theory #haskell
❤3
🍃Чистота функций
В чем суть чистоты функций, если современные системы выполняют очень много действий, создающих побочные эффекты?
Как я уже писал в посте про основополагающие принципы функционального программирования, при вызове чистой функции её результат зависит только от входных параметров. С одними и теми же параметрами чистая функция возвращает одно и то же значение, никак не влияя на внутреннее состояние системы. Это два свойства чистых функций – детерминированность и идемпотентность соответственно. Функции, нарушающие одно из свойств (или оба), имеют некоторые побочные эффекты, размазывающие результат функции по системе: меняют локальное состояние (например, записывают данные в локальную БД), обращаются к внешним ресурсам, обращение к которым может завершиться ошибкой, или возвращающим переменные данные (HTTP-запросы, обращения к памяти, вывод в консоль, получение timestamp и так далее). В терминологии Haskell для таких операций есть отдельное название – действие ввода-вывода. Но сегодня не будем про Haskell, как-нибудь в следующий раз.
Любая система обращается к внешним ресурсам и возвращает некоторый результат обработки данных. Так зачем запариваться, если побочные эффекты неизбежны?
Суть чистоты функций и функционального программирования не в тотальном уничтожении побочных эффектов, а в их жестком контроле, изоляции и управлении. Это стратегия "разделяй и властвуй". Совсем исключить их невозможно, однако мы можем вытеснить их на самую периферию нашей системы.
Идеальная архитектура приложения в этом смысле выглядит так:
1. Ядро: Полностью чистое. Это бизнес-логика, алгоритмы, преобразования данных. Оно ничего не знает о базах данных, HTTP-запросах или файловой системе. Оно просто принимает данные и возвращает результат. Это самая ценная и стабильная часть приложения.
2. Оболочка: Тонкий слой, отвечающий за взаимодействие с внешним миром. Он получает сырые данные "снаружи" (из БД, от API-вызова), прогоняет их через чистое ядро и затем (или перед этим) выполняет необходимые эффекты: запись в БД, отправку ответа, логирование.
Вдобавок, чистые функции сильно проще тестировать, что вытекает из её свойств - результат выполнения такой функции зависит исключительно от входных данных. Это значит, что не нужно поднимать моки БД, устанавливать специальное состояние системы, симулировать сетевые запросы и так далее - тесты становятся простыми, так как здесь работает принцип "чёрного ящика": мы просто передаем определенные значения и проверяем корректность результата, не привязываясь к реализации тестируемой функции. Более того, чистые функции являются потокобезопасными, и результат их применения очень легко мемоизировать (кешировать по входным параметрам), что даёт колоссальную прибавку производительности.
Такой подход превращает написание кода из хаотичного процесса, где всё связано со всем, в рациональный и упорядоченный, где большая часть системы ведёт себя предсказуемо и поддаётся простому анализу. Это не запаривание ради запаривания, а инвестиция в поддержку, надёжность и скорость разработки проекта в долгосрочной перспективе. Да, приходится подумать лишний раз, но оно окупится с лихвой.
#functional #theory
В чем суть чистоты функций, если современные системы выполняют очень много действий, создающих побочные эффекты?
Как я уже писал в посте про основополагающие принципы функционального программирования, при вызове чистой функции её результат зависит только от входных параметров. С одними и теми же параметрами чистая функция возвращает одно и то же значение, никак не влияя на внутреннее состояние системы. Это два свойства чистых функций – детерминированность и идемпотентность соответственно. Функции, нарушающие одно из свойств (или оба), имеют некоторые побочные эффекты, размазывающие результат функции по системе: меняют локальное состояние (например, записывают данные в локальную БД), обращаются к внешним ресурсам, обращение к которым может завершиться ошибкой, или возвращающим переменные данные (HTTP-запросы, обращения к памяти, вывод в консоль, получение timestamp и так далее). В терминологии Haskell для таких операций есть отдельное название – действие ввода-вывода. Но сегодня не будем про Haskell, как-нибудь в следующий раз.
Любая система обращается к внешним ресурсам и возвращает некоторый результат обработки данных. Так зачем запариваться, если побочные эффекты неизбежны?
Суть чистоты функций и функционального программирования не в тотальном уничтожении побочных эффектов, а в их жестком контроле, изоляции и управлении. Это стратегия "разделяй и властвуй". Совсем исключить их невозможно, однако мы можем вытеснить их на самую периферию нашей системы.
Идеальная архитектура приложения в этом смысле выглядит так:
1. Ядро: Полностью чистое. Это бизнес-логика, алгоритмы, преобразования данных. Оно ничего не знает о базах данных, HTTP-запросах или файловой системе. Оно просто принимает данные и возвращает результат. Это самая ценная и стабильная часть приложения.
2. Оболочка: Тонкий слой, отвечающий за взаимодействие с внешним миром. Он получает сырые данные "снаружи" (из БД, от API-вызова), прогоняет их через чистое ядро и затем (или перед этим) выполняет необходимые эффекты: запись в БД, отправку ответа, логирование.
Вдобавок, чистые функции сильно проще тестировать, что вытекает из её свойств - результат выполнения такой функции зависит исключительно от входных данных. Это значит, что не нужно поднимать моки БД, устанавливать специальное состояние системы, симулировать сетевые запросы и так далее - тесты становятся простыми, так как здесь работает принцип "чёрного ящика": мы просто передаем определенные значения и проверяем корректность результата, не привязываясь к реализации тестируемой функции. Более того, чистые функции являются потокобезопасными, и результат их применения очень легко мемоизировать (кешировать по входным параметрам), что даёт колоссальную прибавку производительности.
Такой подход превращает написание кода из хаотичного процесса, где всё связано со всем, в рациональный и упорядоченный, где большая часть системы ведёт себя предсказуемо и поддаётся простому анализу. Это не запаривание ради запаривания, а инвестиция в поддержку, надёжность и скорость разработки проекта в долгосрочной перспективе. Да, приходится подумать лишний раз, но оно окупится с лихвой.
#functional #theory
🔥4👍1
О чем рассказать в следующем посте?
Anonymous Poll
69%
Как я учусь
44%
RAG - как это ведать
38%
Почему кругозор очень важен
К слову, у меня есть лайв-канал – там щитпост, мемы, собаки, ремонт на коленке и другие аспекты моей жизни. Сейчас начался танцевальный сезон, поэтому всяких танцулек там будет много
https://t.me/chrisonych
https://t.me/chrisonych
Telegram
ЧЁРНЫЙ МЕНЧИК
Папапевегимабоди
this :: IO Diary pinned «К слову, у меня есть лайв-канал – там щитпост, мемы, собаки, ремонт на коленке и другие аспекты моей жизни. Сейчас начался танцевальный сезон, поэтому всяких танцулек там будет много https://t.me/chrisonych»
В дополнение к текстовым постам я также буду вести заметки по разработке, так как у меня появилась хорошая идея для OSS-проекта, к реализации которой я приступлю с понедельника.
Я играю на бас-гитаре, и сосед-гитарист пригласил меня играть вместе с ним — вот мы уже ко второму совместному квартирнику готовимся. Если раньше подобранные на слух несложные партии я записывал на бумагу, то сейчас я не знаю, как держать это всё на бумаге так, чтобы можно было разобратьсябез 100 грамм, просто бегло взглянув на схему. К тому же партии сложные, и держать все в голове достаточно тяжело.
Я нашел в пару сервисов, позволяющих удобно создавать табы. Но ограничение в 15 песен меня смущает😃
Коротко об идее: хочу дойти до self-hosted решения для организации и хранения репертуара и написания табулатуры. В веб-интерфейсе можно будет прописывать табулатуру для гитары и бас-гитары, сохранять её и структурировать в плейлисты. Прямо в редакторе можно будет прослушивать табулатуру в синтезе
Я играю на бас-гитаре, и сосед-гитарист пригласил меня играть вместе с ним — вот мы уже ко второму совместному квартирнику готовимся. Если раньше подобранные на слух несложные партии я записывал на бумагу, то сейчас я не знаю, как держать это всё на бумаге так, чтобы можно было разобраться
Я нашел в пару сервисов, позволяющих удобно создавать табы. Но ограничение в 15 песен меня смущает😃
Коротко об идее: хочу дойти до self-hosted решения для организации и хранения репертуара и написания табулатуры. В веб-интерфейсе можно будет прописывать табулатуру для гитары и бас-гитары, сохранять её и структурировать в плейлисты. Прямо в редакторе можно будет прослушивать табулатуру в синтезе
🔥2👍1
