возможно ли функциональное программирование gui?

<p>недавно я обнаружил ошибку fp (пытаясь выучить haskell), и меня по-настоящему поразило то, что я видел до сих пор (первоклассные функции, ленивая оценка и все другие вкусности) ). я еще не эксперт, но я уже начал находить, что проще рассуждать "функционально", чем обязательно для базовых алгоритмов (и мне трудно вернуться туда, где я должен).</p><p>однако единственная область, в которой текущая fp, кажется, падает, - это программирование с помощью графического интерфейса. подход haskell, по-видимому, заключается в том, чтобы просто обернуть обязательные наборы инструментов gui (такие как gtk + или wxwidgets) и использовать блоки "do" для имитации императивного стиля. я не использовал f #, но, насколько я понимаю, он делает нечто подобное, используя oop с классами .net. очевидно, что для этого есть веская причина - в современном программировании с графическим интерфейсом все о io и побочных эффектах, поэтому чисто функциональное программирование невозможно с большинством современных сред.</p><p>у меня вопрос, возможно ли иметь функциональный подход к программированию gui? у меня проблемы с представлением, как это будет выглядеть на практике. кто-нибудь знает какие-либо фреймворки, экспериментальные или иные, которые пробуют подобные вещи (или даже какие-либо фреймворки, которые изначально разрабатывались для функционального языка)? или решение состоит в том, чтобы просто использовать гибридный подход с oop для частей графического интерфейса и fp для логики? (я просто из любопытства спрашиваю - я хотел бы думать, что fp - это "будущее", но программирование в gui кажется довольно большой дырой, которую нужно заполнить.)</p>

<blockquote> <p>у меня вопрос, возможно ли иметь функциональный подход к программированию gui?</p> </blockquote><p>ключевые слова, которые вы ищете, это "функционально-реактивное программирование" (frp).</p><p>конал эллиотт и некоторые другие создали небольшую индустрию, пытаясь найти подходящую абстракцию для frp. существует несколько реализаций концепций frp в haskell.</p><p>вы могли бы начать с самой последней статьи конала <a href="http://conal.net/papers/push-pull-frp/">"push-pull functional reactive programming"</a> , но есть несколько других (более старых) реализаций, некоторые из которых связаны с <a href="http://www.haskell.org/frp/">сайт haskell.org</a> , у конала есть ловкость для охвата всего домена, и его статья может быть прочитана без ссылки на то, что было раньше.</p><p>чтобы понять, как этот подход можно использовать для разработки графического интерфейса, вы можете взглянуть на <a href="http://www.cs.chalmers.se/cs/research/functional/fudgets/">fudgets</a> , который в наши дни становится немного длиннее, разрабатываясь в середине 90-х годов. , представляет надежный подход frp к дизайну gui.</p>

<p><strong>windows presentation foundation</strong> является доказательством того, что функциональный подход очень хорошо работает для программирования gui. он имеет много функциональных аспектов, и "хороший" код wpf (поиск шаблона mvvm) подчеркивает функциональный подход над императивным. я мог бы смело утверждать, что wpf является самым успешным в мире функциональным инструментарием gui :-)</p><p>wpf описывает пользовательский интерфейс в xaml (хотя вы можете переписать его так, чтобы он также выглядел функционально на c # или f #), поэтому для создания некоторого пользовательского интерфейса вы должны написать:</p><pre><code><!-- declarative user interface in wpf and xaml --> <canvas background="black"> <ellipse x:name="greenellipse" width="75" height="75" canvas.left="0" canvas.top="0" fill="lightgreen" /> </canvas> </code></pre><p>кроме того, wpf также позволяет декларативно описывать анимации и реакции на события, используя другой набор декларативных тегов (опять же, то же самое можно записать в виде кода c #/f #):</p><pre><code><doubleanimation storyboard.targetname="greenellipse" storyboard.targetproperty="(canvas.left)" from="0.0" to="100.0" duration="0:0:5" /> </code></pre><p>на самом деле, я думаю, что wpf имеет много общего с frp на haskell (хотя я считаю, что дизайнеры wpf не знали о frp, и это немного досадно - wpf иногда чувствует себя немного странно и неясно, используете ли вы функционал точка зрения).</p>

<p>я бы на самом деле сказал, что функциональное программирование (f #) является гораздо лучшим инструментом для программирования пользовательского интерфейса, чем, например, c #. вам просто нужно думать о проблеме немного по-другому.</p><p>я обсуждаю эту тему в книге <a href="http://functional-programming.net">мое функциональное программирование</a> в главе 16, но есть <a href="http://dotnetslackers.com/articles/net/programming-user-interfaces-using-f-sharp-workflows.aspx">доступна бесплатная выдержка</a> , которая показывает (imho) наиболее интересный шаблон, который вы можете использовать в f # , скажем, вы хотите реализовать рисование прямоугольников (пользователь нажимает кнопку, перемещает мышь и отпускает кнопку). в f # вы можете написать что-то вроде этого:</p><pre><code>let rec drawingloop(clr, from) = async { // wait for the first mousemove occurrence let! move = async.awaitobservable(form.mousemove) if (move.button &&& mousebuttons.left) = mousebuttons.left then // refresh the window & continue looping drawrectangle(clr, from, (move.x, move.y)) return! drawingloop(clr, from) else // return the end position of rectangle return (move.x, move.y) } let waitingloop() = async { while true do // wait until the user starts drawing next rectangle let! down = async.awaitobservable(form.mousedown) let downpos = (down.x, down.y) if (down.button &&& mousebuttons.left) = mousebuttons.left then // wait for the end point of the rectangle let! uppos = drawingloop(color.indianred, downpos) do printfn "drawn rectangle (%a, %a)" downpos uppos } </code></pre><p>это очень обязательный подход (в обычном прагматическом стиле f #), но он избегает использования изменяемого состояния для сохранения текущего состояния рисования и для сохранения исходного местоположения. это может быть сделано еще более функциональным, хотя я написал библиотеку, которая делает это как часть моей магистерской диссертации, которая должна быть доступна на <a href="http://tomasp.net/blog">мой блог</a> в ближайшие пару дней.</p><p>функциональное реактивное программирование является более функциональным подходом, но я нахожу его несколько сложнее в использовании, поскольку оно опирается на довольно продвинутые функции haskell (такие как стрелки). тем не менее, это очень элегантно в большом количестве случаев. ограничение состоит в том, что вы не можете легко закодировать конечный автомат (это полезная ментальная модель для реактивных программ). это очень легко, используя технику f # выше.</p>

<p>независимо от того, используете ли вы гибридный функциональный/oo-язык, такой как f # или ocaml, или чисто функциональный язык, такой как haskell, где побочные эффекты относятся к монаде io, это <em> в основном </em> случай, когда тонна работы, требуемой для управления графическим интерфейсом, во многом напоминает "побочный эффект", а не чисто функциональный алгоритм.</p><p>тем не менее, были проведены некоторые действительно серьезные исследования в <a href="http://haskell.org/jcp/hw04.pdf">функциональные графические интерфейсы</a> . есть даже некоторые (в основном) функциональные наборы инструментов, такие как <a href="http://en.wikipedia.org/wiki/fudgets">fudgets</a> или <a href="http://www.dcs.gla.ac.uk/~meurig/research/icfp.pdf">frantk</a> .</p>

<p>вы можете проверить сериал дона сайма на f #, где он демонстрирует создание графического интерфейса. следующая ссылка - на третью часть серии (оттуда вы можете перейти на две другие части).</p><p>использование f # для разработки wpf было бы очень интересной парадигмой gui ...</p><p> <a href="http://channel9.msdn.com/shows/going+deep/c9-lectures-dr-don-syme-introduction-to-f-3-of-3/">http://channel9.msdn.com/shows/going+deep/c9-lectures-dr-don-syme-introduction-to-f-3-of-3/</a> </p>

<p>одна из идей, открывающих разум в основе функционально-реактивного программирования, состоит в том, чтобы иметь функцию обработки событий, производящую оба реакцию на события и следующую функцию обработки событий. таким образом, развивающаяся система представляется в виде последовательности функций обработки событий.</p><p>для меня изучение yampa стало решающим фактором для правильного понимания этой функции. есть несколько хороших статей о ямпе. я рекомендую the yampa arcade:</p><p> <a href="http://www.cs.nott.ac.uk/~nhn/talks/hw2003-yampaarcade.pdf">http://www.cs.nott.ac.uk/~nhn/talks/hw2003-yampaarcade.pdf</a> (слайды, pdf) <a href="http://www.cs.nott.ac.uk/~nhn/publications/hw2003.pdf">http: //www.cs.nott. ac.uk/~nhn/publications/hw2003.pdf</a> (полная статья, pdf)</p><p>на ямпе есть вики-страница на haskell.org</p><p> <a href="http://www.haskell.org/haskellwiki/yampa">http://www.haskell.org/haskellwiki/yampa</a> </p><p>оригинальная домашняя страница yampa:</p><p> <a href="http://www.haskell.org/yampa">http://www.haskell.org/yampa</a> (к сожалению, в данный момент не работает)</p>

<p>с тех пор как этот вопрос был впервые задан, элм сделал функциональное реактивное программирование немного более распространенным.</p><p>я предлагаю проверить это по адресу <a href="http://elm-lang.org">http://elm-lang.org</a> , где также есть несколько действительно отличных интерактивных руководств по созданию полнофункционального графического интерфейса в браузере.</p><p>это позволяет вам создавать полностью функциональные графические интерфейсы, где код, который вам нужно предоставить, состоит только из чистых функций. лично мне было намного проще в этом разобраться, чем в различных gui-средах haskell.</p>

<p>доклад эллиота о frp можно найти <a href="http://vimeo.com/6686570">здесь</a> .</p><p><strong> кроме того, на самом деле не ответ, а замечание и несколько мыслей </strong>: каким-то образом термин "функциональный графический интерфейс" немного напоминает оксюморон (чистота и io в том же сроке).</p><p>но мое смутное понимание состоит в том, что функциональное программирование gui - это декларативное определение функции, зависящей от времени, которая принимает (реальный) зависящий от времени пользовательский ввод и производит зависящий от времени вывод gui.</p><p>другими словами, эта функция определяется декларативно, как дифференциальное уравнение, а не алгоритмом, обязательно использующим изменяемое состояние.</p><p>таким образом, в обычном fp используются независимые от времени функции, а в frp используются зависящие от времени функции в качестве строительных блоков для описания программы.</p><p>давайте подумаем о моделировании мяча на пружине, с которым пользователь может взаимодействовать. положение шара - это графический вывод (на экране), пользователь, нажимающий на шар, - это нажатие клавиши (ввод).</p><p>описание этой программы моделирования в frp (согласно моему пониманию) выполняется одним дифференциальным уравнением (декларативно): ускорение * масса = - растяжение пружины * постоянная пружины + сила, прилагаемая пользователем.</p><p>вот видео на <a href="http://www.infoq.com/presentations/elm-reactive-programming">elm</a> , которое иллюстрирует эту точку зрения.</p>

<p>языки разметки, такие как xul, позволяют создавать gui декларативным способом.</p>

<p>чтобы решить эту проблему, я написал несколько своих соображений по поводу использования f #,</p><p> <a href="http://fadsworld.wordpress.com/2011/04/13/f-in-the-enterprise-i/">http://fadsworld.wordpress.com/2011/04/13/f-in-the-enterprise-i/</a> http://fadsworld.wordpress.com/2011/04/17/fin -the-предприятие-ii-2 /</p><p>я также планирую сделать видеоурок, чтобы закончить серию и показать, как f # может внести вклад в программирование ux.</p><p>я говорю только в контексте f # здесь.</p><p>-fahad</p>

<p>все эти другие ответы основаны на функциональном программировании, но принимают множество собственных дизайнерских решений. одна библиотека, которая построена в основном полностью из функций и простых абстрактных типов данных: <a href="http://hackage.haskell.org/package/gloss"><code>gloss</code></a> . вот тип для его <a href="http://hackage.haskell.org/package/gloss/docs/graphics-gloss-interface-pure-game.html#v:play"><code>play</code></a> функции из источника</p><pre class="lang-hs prettyprint-override"><code>-- | play a game in a window. like `simulate`, but you manage your own input events. play :: display -- ^ display mode. -> color -- ^ background color. -> int -- ^ number of simulation steps to take for each second of real time. -> world -- ^ the initial world. -> (world -> picture) -- ^ a function to convert the world a picture. -> (event -> world -> world) -- ^ a function to handle input events. -> (float -> world -> world) -- ^ a function to step the world one iteration. -- it is passed the period of time (in seconds) needing to be advanced. -> io () </code></pre><p>как вы можете видеть, он работает полностью, обеспечивая чистые функции простыми абстрактными типами, с которыми вам могут помочь другие библиотеки.</p>

<p>наиболее очевидным нововведением, замеченным людьми, плохо знакомыми с хаскеллом, является то, что существует некое разделение между нечистым миром, связанным с общением с внешним миром, и чистым миром вычислений и алгоритмов. частый вопрос для начинающих: "как мне избавиться от <code>io</code>, т.е. преобразовать <code>io a</code> в <code>a</code>?" путь к этому - использовать монады (или другие абстракции) для написания кода, который выполняет io и цепочку эффектов. этот код собирает данные из внешнего мира, создает его модель, выполняет некоторые вычисления, возможно, используя чистый код, и выводит результат.</p><p>что касается приведенной выше модели, я не вижу ничего страшного в манипулировании gui в монаде <code>io</code>. самая большая проблема, которая возникает из-за этого стиля, состоит в том, что модули больше не могут быть компонуемы, то есть я теряю большую часть моих знаний о глобальном порядке выполнения операторов в моей программе. чтобы восстановить его, я должен применить те же рассуждения, что и в параллельном императивном коде gui. между тем, для нечистого, не gui-кода порядок выполнения очевиден из-за определения оператора <code>>==</code> монады <code>io</code> (по крайней мере, пока существует только один поток). для чистого кода это вообще не имеет значения, кроме как в угловых случаях для повышения производительности или во избежание оценок, приводящих к <code>⊥</code>.</p><p>самое большое философское различие между консольным и графическим io заключается в том, что программы, реализующие первое, обычно написаны в синхронном стиле. это возможно, потому что есть (оставляя в стороне сигналы и другие дескрипторы открытого файла) только один источник событий: поток байтов, обычно называемый <code>stdin</code>. хотя gui по своей сути асинхронны и должны реагировать на события клавиатуры и щелчки мыши.</p><p>популярная философия выполнения асинхронного io функциональным способом называется функционально-реактивным программированием (frp). в последнее время он получил широкое распространение в нечистых, нефункциональных языках благодаря библиотекам, таким как <a href="http://reactivex.io/">reactivex</a> , и фреймворкам, таким как elm. в двух словах, это похоже на просмотр элементов графического интерфейса пользователя и других вещей (таких как файлы, часы, сигналы тревоги, клавиатура, мышь) в качестве источников событий, называемых "наблюдаемыми", которые излучают потоки событий. эти события объединяются с использованием знакомых операторов, таких как <code>map</code>, <code>foldl</code>, <code>zip</code>, <code>filter</code>, <code>concat</code>, <code>join</code> и т.д., для создания новых потоков. это полезно, потому что само состояние программы можно рассматривать как <code>scanl . map reacttoevents $ zipn <eventstreams></code> программы, где <code>n</code> равно числу наблюдаемых, когда-либо рассматриваемых программой.</p><p>работа с наблюдаемыми frp позволяет восстановить возможность компоновки, поскольку события в потоке упорядочены во времени. причина в том, что абстракция потока событий позволяет просматривать все наблюдаемые как черные ящики. в конечном счете, объединение потоков событий с использованием операторов возвращает некоторое локальное упорядочение при выполнении. это заставляет меня быть более честным в отношении того, на какие инварианты на самом деле опирается моя программа, подобно тому, как все функции в haskell должны быть прозрачными по ссылкам: если я хочу получить данные из другой части моей программы, я должен быть явным объявление объявляет соответствующий тип для моих функций. (монада io, будучи предметно-ориентированным языком для написания нечистого кода, эффективно обходит это)</p>