Привет! Меня зовут Ксюша, я работаю бэкенд-разработчиком на C# в Контуре. В весеннем семестре я провела курс по функциональному программированию на Haskell для студентов 3 и 4 курса направления ФИИТ (фундаментальная информатика и информационные технологии) в УрФУ и хочу поделиться своим опытом организации и проведения курса.   

С чего всё началось

Как же так вышло, что я пошла рассказывать студентам про монады и трансформеры (не те, которые Attention is all you need!)?

Я сама училась на ФИИТе, и курс по функциональному программированию (ФП) у нас был обязательным, но в наш год не нашли преподавателей, поэтому мы изучали его самостоятельно.

Мне ФП выворачивало мозг! Особенно меня удивил подход с лямбда-калькулятором. Попробуйте проверить число на то, что оно 0, имея только лямбды в распоряжении. А это мы еще про рекурсию не говорили.

isZero = \n.(n (\x.false) true)

И почему вообще булевы значения — это тоже какие-то функции? В общем, мне было это очень интересно, но много материала я не поняла в момент прохождения или плохо научилась применять. Мне помнится, что я поплыла на теме трансформеров и продвинутых монад. Очень хотелось разобраться в концепциях функционального программирования поподробнее, а какой лучший способ разобраться в чем-либо? Объяснить это другому человеку! Вот я и решила преподавать.  

Что представляет из себя курс 

«Введение в функциональное программирование» — это курс в формате перевернутого класса: дома студенты смотрят лекции и решают задачи на платформе ulearn, а потом подключаются на семинар с преподавателем, на котором более подробно разбирается пройденная тема.

Курс знакомит слушателей с концепциями, лежащими под ФП, и тем, как это реализовано в Haskell. Вот полный список тем:

  1. Знакомство с Haskell

  2. Списки

  3. Лямбда-исчисление

  4. Алгебраические типы данных

  5. Типы и полиморфизм в лямбда-исчислении

  6. Ленивые вычисления

  7. Эффекты и монады

  8. Практикум

  9. Классы типов

  10. Структуры данных

  11. Остальные монады

  12. Трансформеры

  13. Тестирование в Haskell

  14. Type-level programming

  15. Пример веб-приложения

Например, вот какую задачу я давала студентам по теме «Трансформеры»: нужно написать функцию, которая проверит, скомпрометирован ли пароль. Звучит просто, не так ведь? 

Однако задача подразумевает чтение базы скомпрометированных паролей (база для простоты является текстовым файлом), а также запись в лог о результате проверки. Все еще звучит просто? Загвоздка в том, что функциональное программирование оперирует чистыми функциями — функциями без сайд-эффектов. А как же тогда нам вообще работать с вводом и выводом? Чтобы дойти до того, чтобы в одной функции осуществить чтение и запись, понадобился почти весь семестр и много изученных концепций из мира функциональщины :)

Так что курс нужен не для обучения промышленной разработке, а для расширения кругозора и общего развития. Чтобы не заучить, как сделать функцию с чтением и записью, а понять, почему она выглядит так, как выглядит, а упростить нельзя.

Математику уже затем учить надо, что она ум в порядок приводит.

(с) Ломоносов М.В.

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

Грабли 

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

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

Спрос оказался больше 

Сначала нужно было привлечь студентов на курс. Я написала анонс, добавила мемов про ФП, но была уверена, что придет на курс максимум человек 15-20, поэтому не ограничила набор на курс. Ну и чем же это обернулось? На курсе 60 человек и один преподаватель! 

Это я, когда поняла, как будет выглядеть моя весна
Это я, когда поняла, как будет выглядеть моя весна

Ну… не все так плохо. Я знала, что на семинары будет ходить гораздо меньше людей, поэтому за проведение пар не особо волновалась, но вот с домашками была беда. Проверять даже 15 работ в неделю — достаточно тяжело, а это плюсом к тому, что надо самой вспомнить и/или изучить материал (мы же помним, что я пошла преподавать, чтобы разобраться в теме) и подготовиться к паре. 

Мне повезло, потому что мои бывшие однокурсники согласились помогать проверять домашки. Спасибо им! Без них я бы не вывезла уже на второй неделе. В итоге мы разделили студентов примерно поровну и должны были проверять примерно по 15 работ каждый. Но здесь все прошло не так хорошо, как хотелось бы — в середине семестра ребят догнали разные важные вещи. У кого-то это был запланированный отпуск, у кого-то — организация контеста для отбора на стажировку. 

Еще добавлю, что в середине семестра была тема с объемной домашней работой. Вникать в такие работы было тяжело. Ревью на отвали делать совсем не хочется. Как результат, с середины семестра домашки у многих студентов проверялись очень медленно. 

Это был основной минус курса, который отметили студенты. Вывод, который мы сделали: в следующий раз ограничиваем количество проверяемых работ в неделю 10 штуками на человека, следовательно, набор ограничиваем 10 \* количество преподавателей. Скорее всего, нас снова будет четверо, поэтому набор будет 40 человек.   

Спешное начало курса

Т. к. курс стартанул достаточно быстро, то тестового прогона у нас не было. Да и зачем, казалось мне, ведь ровно на этой же платформе, студенты 3-4 года назад сдавали задачи. И все было нормально!

Конечно же, и тут меня ждал сюрприз, а именно — неработающая тестирующая система!

Лямбда-исчисление поджидает нас в первой половине курса
Лямбда-исчисление поджидает нас в первой половине курса

В первый раз, когда в чат студент прислал скриншот с серым сообщением от сервера, оказалось, что у ulearn прилегли гитлаб-раннеры, которые и проверяют решения студентов.

Ну тут все просто — ждем, когда разработчики ulearn все пофиксят на своей стороне и спокойно ждем, благо дедлайны можно и немного сдвинуть.

Когда мне написали то же самое в другой день, я уже была на опыте и знала, что буду делать — переложу ответственность на разработчиков ulearn! Но не тут-то было…

Неполадки в первый раз были при сдаче задачи на хаскеле, а вот во второй раз — при сдаче задачи по лямбда-исчислению, для которого мы используем mikrokosmos. Как работает тестирующая система: у задачи есть Dockerfile, из которого собирается образ для тестирования. В нашем случае нужно было установить haskell и mikrokosmos, потому что он написан на haskell. Далее в рабочую директорию копируется решение студента и прогоняются тесты. Вот только образ с микрокосмосом перестал собираться… Оказалось, что используемый образ хаскеля для сборки микрокосмоса устарел и теперь не устанавливается. Ну легко, просто поднимаем версию хаскеля, да и все. Так я и сделала и получила ошибки при компиляции микрокосмоса. В хаскеле сломана обратная совместимость (тред на stackoverflow с такой же проблемой), а значит нужно поднять и версию микрокосмоса. Заходим на страницу этого космического калькулятора, а там последняя версия 0.8.0. А она-то под старую версию и заточена!

Скрин со страницы микрокосмоса на hackage
Скрин со страницы микрокосмоса на hackage

В этот момент я уже совсем было расстроилась: несмотря на то, что локально решения все еще можно было проверить, т.к. был собранный образ на Dockerhub, нужно было бы придумывать, как проверить решения студентов -- перед ревью еще запускать локальный прогон, или временно забить на задачу, или подредактировать регламент курса?

Но внимательный пользователь, а таким оказался Женя, который тоже проверял домашки, зайдет еще дополнительно на гитхаб и увидит, что последняя версия все-таки 0.8.1 и есть Issue для поднятия версии ?

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

Finita la commedia, друзья

Почему так произошло?

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

Домашки делают нейросети 

Еще одна проблема, с которой мы столкнулись — использование GPT. Чрезмерное использование GPT. Ооооочень много работ списаны, причем есть и те, которые списаны достаточно неумело — такие работы сразу видно. На ulearn есть система антиплагиата, но все-таки списать можно так, что антиплагиат не сработает.   

Я понимаю, что использование нейросеток неизбежно, поэтому одной из задач, которые нужно будет решить до следующей весны — как адаптировать задачи под их использование? У нас есть несколько идей, как это сделать, но пока они не проработаны.

Если у вас есть опыт адаптации курса под GPT или идеи того, как это сделать, то буду рада, если вы поделитесь ими в комментариях!    

Что получилось хорошо

Что-то я про то, что не получилось… А что же получилось?   

Первые несколько пар мы со студентами работали в общем зале, пытались обсуждать материал и решать небольшие задачки. Но в онлайне люди разговаривали не особо активно, а мне хотелось, чтобы больше людей были вовлечены в процесс. 

Поэтому я внедрила механику с работой в сессионных залах — даю студентам задачки на подумать, но не рокет-сайенс. Они работают над ними в группах по 3-4 человека, а потом мы все собираемся в общем зале, чтобы обсудить решения. 

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

Меня очень радовали студенты, которые активничали и общались со мной на парах, у которых получалось докопаться до того, как сделать так, чтобы сошлись типы в функции с трансформерами.   

Мнение самих студентов

Получилось собрать не очень много обратной связи от студентов, но тем не менее курс им, по большей части, понравился. Особенно зашла механика с работой в группах в целом нестандартность курса на фоне «обычного» программирования. Ещё студенты отметили, что курс заставил их поломать голову и подумать в другой плоскости. Я рада, что кому-то понравилось и у меня получилось кого-то заинтересовать!   

Спасибо всем, кто дочитал до конца. Если у вас есть опыт в ФП, а особенно в Haskell, и вам интересно поделиться своим опытом, то буду рада контактам!   

Всем монад)


Воспользуемся моментом и обратимся к тем, у кого есть дети-выпускники, или друзья, которые прямо сейчас выбирают вуз. До 25 июля можно подавать заявления на бюджет. ФИИТ на матмехе УрФУ — отличное ИТ-направление, а Екатеринбург — крутой город для студенческой жизни. Все подробности и нужные ссылки в ВК https://vk.com/fiiturfu. Смело пересылайте знакомым абитуриентам!

Комментарии (1)


  1. tkutru
    22.07.2025 16:54

    Нормальная тема!

    Ксюша, было прикольно если бы ты обрисовала основные идеи курса/ФП в отдельном посте. Тем более что по твоей основной работе бекенд на си шарпе это по идее должен быть сплошной ООП. Как ты докатилась до такого? :) успехов