Возможно ли функциональное программирование графического интерфейса? [закрытый]


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

единственная область, в которой ток ФП, казалось бы, падать плашмя, это интерфейс программирования. Подход Хаскелла кажется, чтобы просто обернуть императивные наборы инструментов GUI (такие как GTK+ или wxWidgets) и использовать блоки "do" для имитации императивного стиля. Я не использовал F#, но я понимаю, что он делает что-то подобное, используя ООП с классами .NET. Очевидно, что для этого есть веская причина-текущее Программирование GUI-это все о IO и побочных эффектах, поэтому чисто функциональное программирование невозможно с большинством текущих фреймворков.

мой вопрос в том, можно ли иметь функциональный подход для программирования GUI? Мне трудно представить, как это будет выглядеть на практике. Кто-нибудь знает о каких-либо фреймворках, экспериментальных или других, которые пробуют такие вещи (или даже любые фреймворки, разработанные с нуля для функционального языка)? Или это решение просто использовать гибридный подход, с ООП для частей GUI и FP для логики? (Я просто спрашиваю из любопытства-мне бы хотелось думать, что FP-это "будущее", но Программирование GUI кажется довольно большим отверстие для заполнения.)

15 375

15 ответов:

подход Haskell, похоже, заключается в том, чтобы просто обернуть императивные наборы инструментов GUI (такие как GTK+ или wxWidgets) и использовать блоки "do" для имитации императивного стиля

это не совсем "подход Хаскелла" - это просто то, как вы привязываетесь к императивным инструментам GUI напрямую-через императивный интерфейс. У Хаскелла просто есть довольно заметные привязки.

есть несколько умеренно зрелых, или более экспериментальных чисто функциональные / декларативные подходы к ГИП, в основном в Haskell, и в первую очередь с использованием функционального реактивного программирования.

некоторые примеры:

для тех из вас, кто не знаком с Хаскеллом, Flapjax,http://www.flapjax-lang.org/ - это реализация функционального реактивного программирования поверх JavaScript.

мой вопрос в том, можно ли иметь функциональный подход к программированию GUI?

ключевые слова, которые вы ищете, - это "функциональное реактивное программирование" (FRP).

Конел Эллиот и некоторые другие сделали немного кустарной промышленности из попыток найти правильную абстракцию для FRP. Существует несколько реализаций концепций FRP в Haskell.

вы можете рассмотреть возможность начать с самого последнего " Push-Pull Функциональное Реактивное Программирование" бумага, но есть несколько других (более старых) реализаций, некоторые из которых связаны с haskell.org сайт. У Конела есть талант охватывать всю область, и его статью можно прочитать без ссылки на то, что было раньше.

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

Windows Presentation Foundation является доказательством того, что функциональный подход работает очень хорошо для GUI-программирования. Он имеет много функциональных аспектов и "хороший" код WPF (поиск шаблона MVVM) подчеркивает функциональный подход более важно. Я мог бы смело утверждать, что WPF является самым успешным реальным функциональным GUI toolkit: -)

WPF описывает пользовательский интерфейс в XAML (хотя вы можете переписать его на функционально выглядящий C# или F# тоже), поэтому для создания некоторых пользовательский интерфейс вы бы написали:

<!-- 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>

кроме того, WPF также позволяет декларативно описывать анимации и реакции на события, используя другой набор декларативных тегов (опять же, то же самое можно записать как код C#/F#):

<DoubleAnimation
   Storyboard.TargetName="greenEllipse" 
   Storyboard.TargetProperty="(Canvas.Left)"
   From="0.0" To="100.0" Duration="0:0:5" />

на самом деле, я думаю, что WPF имеет много общего с FRP Haskell (хотя я считаю, что дизайнеры WPF не знали о FRP, и это немного неудачно - WPF иногда чувствует себя немного странно и неясно, если вы используете функциональная точка зрения).

Я бы сказал, что функциональное программирование (F#) является гораздо лучшим инструментом для программирования пользовательского интерфейса, чем, например, C#. Вам просто нужно думать о проблеме немного по-другому.

Я обсуждаю эту тему в мое функциональное программирование книга в главе 16, но есть бесплатный отрывок доступен, который показывает (IMHO) самый интересный шаблон, который вы можете использовать в F#. Скажем, вы хотите реализовать рисование прямоугольников (пользователь нажимает кнопка, перемещает мышь и отпускает кнопку). В F#, вы можете написать что-то вроде этого:

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 }

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

функциональное реактивное программирование является более функциональным подходом, но я нахожу его несколько сложнее использовать, поскольку он опирается на довольно продвинутые функции Haskell (такие как стрелки). Тем не менее, это очень элегантно в большом количестве случаев. Это ограничение заключается в том, что вы не можете легко кодировать машину состояний (которая является полезной ментальной моделью для реактивных программ). Это очень легко, используя технику F# выше.

Если вы находитесь в гибридном функциональном / OO языке, таком как F# или OCaml, или в чисто функциональном языке, таком как Haskell, где побочные эффекты отнесены к монаде IO, это в основном дело в том, что тонна работы, необходимой для управления графическим интерфейсом, гораздо больше похожа на "побочный эффект", чем на чисто функциональный алгоритм.

тем не менее, было проведено действительно серьезное исследование функциональные графические интерфейсы. Есть даже некоторые (в основном) функциональные наборы инструментов например,Fudgets или FranTk.

вы можете проверить серию Дона Сайма на F# , где он демо создает графический интерфейс. следующая ссылка на третью часть серии (вы можете связать оттуда с двумя другими частями).

использование F# для разработчиков WPF будет очень интересная парадигмы графического пользовательского интерфейса...

http://channel9.msdn.com/shows/Going+Deep/C9-Lectures-Dr-Don-Syme-Introduction-to-F-3-of-3/

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

для меня изучение Ямпы стало решающим моментом, чтобы правильно получить эту функцию-производство-функции. Есть несколько хороших газет о Ямпе. Я рекомендую Ямпу Аркада:

http://www.cs.nott.ac.uk/~nhn / Talks / HW2003-YampaArcade. pdf (слайды, PDF) http://www.cs.nott.ac.uk/~nhn / Publications / hw2003.pdf (Полная статья, PDF)

есть вики-страница на Ямпе по адресу Haskell.org

http://www.haskell.org/haskellwiki/Yampa

оригинальная домашняя страница Yampa:

http://www.haskell.org/yampa (к сожалению сломан на данный момент)

разговор Эллиота на FRP можно найти здесь.

кроме того, не ответ, но замечание и несколько мыслей: почему-то термин "функциональный графический интерфейс" кажется немного похожим на оксюморон (pureness и IO в том же термине).

но мое смутное понимание заключается в том, что функциональное программирование GUI-это декларативное определение зависящей от времени функции, которая принимает (реальный)зависящий от времени пользовательский ввод и производит зависящий от времени вывод GUI.

таким образом, в обычном FP используются независимые от времени функции, в то время как в FRP используются зависимые от времени функции в качестве строительных блоков для описания программы.

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

описание этой программы моделирования в FRP (согласно моему пониманию) выполняется одним дифференциальным уравнением (декларативно): ускорение * масса = - растяжение пружины * постоянная пружины + сила, оказываемая пользователем.

вот видео, о Вязовая что иллюстрирует эту точку зрения.

поскольку этот вопрос был впервые задан, функциональное реактивное программирование было сделано немного более распространенным Elm.

Я предлагаю проверить его на http://elm-lang.org, который также имеет некоторые действительно отличные интерактивные учебники о том, как сделать полностью функциональный графический интерфейс в браузере.

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

по состоянию на 2016 год существует еще несколько относительно зрелых фреймворков FRP для Haskell, таких как Sodium и Reflex (но также Netwire).

The книга Мэннинга по функциональному реактивному программированию демонстрирует Java-версию Sodium для рабочих примеров и иллюстрирует, как база кода графического интерфейса FRP ведет себя и масштабируется по сравнению с императивными, а также Акторными подходами.

есть также недавняя статья о Arrowized FRP и перспектива включение побочных эффектов, IO и мутации в законопослушную, чистую настройку FRP: http://haskell.cs.yale.edu/wp-content/uploads/2015/10/dwc-yale-formatted-dissertation.pdf.

также стоит отметить, что JavaScript-фреймворки, такие как ReactJS и Angular и многие другие, либо уже есть, либо движутся в сторону использования FRP или иного функционального подхода к достижению масштабируемых и составных компонентов GUI.

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

чтобы решить эту проблему, я опубликовал некоторые свои мысли в использовании F#,

http://fadsworld.wordpress.com/2011/04/13/f-in-the-enterprise-i/ http://fadsworld.wordpress.com/2011/04/17/fin-the-enterprise-ii-2/

Я также планирую сделать видео-учебник, чтобы закончить серию и показать, как F# может внести свой вклад в Программирование UX.

Я говорю только в контексте F# здесь.

-Фахад

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

IO, т. е. преобразования IO a на a?"Путь к этому - использовать монады (или другие абстракции) для написания кода, который выполняет IO и цепные эффекты. Этот код собирает данные из внешнего мира, создает его модель, выполняет некоторые вычисления, возможно, используя чистый код, и выводит результат.

что касается вышеупомянутой модели, я не вижу ничего ужасного в том, чтобы манипулировать графическими интерфейсами в IO монады. Самая большая проблема, которая возникает из этого стиля, заключается в том, что модули больше не могут быть составлены, т. е. я теряю большую часть своих знаний о глобальном порядке выполнения операторов в моей программе. Чтобы восстановить его, я должен применить аналогичные рассуждения, как в параллельных, императивный графический код. Между тем, для нечистого, не графического кода порядок выполнения очевиден из-за определения IO монады-это >== оператор (по крайней мере, пока существует только один поток). Для чистого кода это вообще не имеет значения, за исключением угловых случаев, чтобы увеличить производительность или избежать оценок, приводящих к .

самое большое философское различие между консольным и графическим IO заключается в том, что программы, реализующие первый, обычно пишутся в синхронный стиль. Это возможно, потому что существует (оставляя в стороне сигналы и другие открытые файловые дескрипторы) только один источник событий: поток байтов, обычно называемый stdin. Однако графические интерфейсы по своей сути асинхронны и должны реагировать на события клавиатуры и щелчки мыши.

популярная философия выполнения асинхронного ввода-вывода функциональным способом называется функциональным реактивным программированием (FRP). В последнее время он получил много тяги в нечистых, нефункциональных языках благодаря библиотекам например,ReactiveX, и рамки, такие как вяз. В двух словах, это похоже на просмотр элементов GUI и других вещей (таких как файлы, часы, сигналы тревоги, клавиатура, мышь) в качестве источников событий, называемых "наблюдаемыми", которые испускают потоки событий. Эти события объединяются с помощью знакомых операторов, таких как map,foldl,zip,filter,concat,join и т. д., чтобы производить новые потоки. Это полезно, потому что само состояние программы можно рассматривать как scanl . map reactToEvents $ zipN <eventStreams> программы, где N равно числу наблюдаемых объектов, когда-либо рассматриваемых программой.

работа с FRP observables позволяет восстановить композиционность, поскольку события в потоке упорядочены во времени. Причина в том, что абстракция потока событий позволяет просматривать все наблюдаемые объекты как черные ящики. В конечном счете, объединение потоков событий с помощью операторов возвращает некоторый локальный порядок выполнения. Это заставляет меня быть гораздо более честным о том, какие инварианты моя программа на самом деле полагается, подобно тому, как все функции в Haskell должны быть ссылочно прозрачными: если я хочу извлечь данные из другой части моей программы, я должен быть явным объявлением объявления соответствующего типа для моих функций. (Монада IO, будучи специфичным для домена языком для написания нечистого кода, эффективно обходит это)

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