Возможно ли программирование графического интерфейса?

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

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

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

Ответ 1

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

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

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

Примером является

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

Ответ 2

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

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

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

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

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

Ответ 3

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

WPF описывает интерфейс пользователя в XAML (хотя вы также можете переписать его на функционально выглядящий С# или 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 также позволяет декларативно описывать анимацию и реакцию на события, используя другой набор декларативных тегов (опять же, то же самое можно записать как код С#/F #):

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

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

Ответ 4

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

Я обсуждаю эту тему в моем функциональном программировании в главе 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 #, описанной выше.

Ответ 5

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

Тем не менее, было проведено несколько действительно твердых исследований в функциональных GUI . Существуют даже некоторые (в основном) функциональные инструментальные средства, такие как Fudgets или FranTk.

Ответ 6

Вы можете проверить серию на Don Syme на F #, где он создает демо-версию gui. следующая ссылка относится к третьей части серии (вы можете связать ее с двумя другими частями).

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

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

Ответ 7

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

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

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

В Yamba на Haskell.org есть страница wiki

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

Оригинальная главная страница Ямпы:

http://www.haskell.org/yampa (к сожалению, сейчас нарушается)

Ответ 8

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

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

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

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

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

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

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

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

Ответ 9

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

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

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

Ответ 10

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

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

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

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

Ответ 11

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

Ответ 12

Чтобы ответить на это, я разместил некоторые мои мысли при использовании 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 # здесь.

-Fahad

Ответ 13

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

-- | 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 ()

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

Ответ 14

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

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

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

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

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

Ответ 15

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