MVU F# как избежать утечки памяти
От: Разраб  
Дата: 17.10.23 03:56
Оценка:
Есть вот такой код.
функция view каждый раз возвращает новый элемент управления, т.к. чистая.
Получается ФП не работает с Winforms? Нужны элементы ООП, чтобы избежать дикого оверхеда?

open System
open System.Windows.Forms

type Update<'Msg, 'Model> = 'Msg -> 'Model -> 'Model
type Dispatch<'Msg> = 'Msg -> obj -> EventArgs -> unit
type View<'Model, 'Msg> = 'Model -> Dispatch<'Msg> -> Control

type Program<'Model, 'Msg>(initialModel: 'Model, view: View<'Model, 'Msg>, update: Update<'Msg, 'Model>) =
    let pump = Event<'Msg * 'Model>()
    let evt = pump.Publish
    let dispatch model msg _ _ = pump.Trigger(msg, model)
    let form = new Form(Text = "OK")

    do
        evt.Add(fun (msg, model) ->
            let newModel = update msg model
            let newLayout = view newModel (dispatch newModel)
            form.Controls.Clear()
            form.Controls.Add(newLayout))

    member __.Run() =
        let initialLayout = view initialModel (dispatch initialModel)
        form.Controls.Add(initialLayout)
        form.ShowDialog()

type Model = int
let initialModel: Model = 0

type Msg =
    | Increment
    | Decrement

let update msg model =
    match msg with
    | Increment -> model + 1
    | Decrement -> model - 1

let view model dispatch =
    let clickCount = new Label(Text = sprintf "Clicked %d times" model)

    let incrButton = new Button(Text = "+")
    let incrClickHandler = EventHandler(dispatch Increment)
    incrButton.Click.AddHandler(incrClickHandler)

    let decrButton = new Button(Text = "-")
    let decrClickHandler = EventHandler(dispatch Decrement)
    decrButton.Click.AddHandler(decrClickHandler)

    let layout = new FlowLayoutPanel(Dock = DockStyle.Fill)
    layout.Controls.Add(clickCount)
    layout.Controls.Add(incrButton)
    layout.Controls.Add(decrButton)

    layout :> Control

let runProgram model view update =
    let main = Program(model, view, update)
    main.Run()

runProgram initialModel view update
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re: MVU F# как избежать утечки памяти
От: samius Япония http://sams-tricks.blogspot.com
Дата: 17.10.23 05:57
Оценка: 3 (1)
Здравствуйте, Разраб, Вы писали:

Р>Есть вот такой код.

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

Р>Получается ФП не работает с Winforms? Нужны элементы ООП, чтобы избежать дикого оверхеда?

Получается, что что бы что-то утверждать, нужно формализовать понятие "ФП" и "работает". Если говорить именно о чистом ФП, то увы, с WinForms его скрестить будет сложно. Однако, F# преподносится гибридным и ФП в его рамках не обязано быть чистым. Компилятор ведь не требует чистоты функций.

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

Собственно, вместо изменения состояния

            form.Controls.Clear()
            form.Controls.Add(newLayout)

вызвать что-то вроде грязной
updateLayout form newModel


Винформс не заведется без грязи. Собственно, стоит ли пытаться делать вид, что программа на ФП с Винформс вся чистая? Но даже если бы оно того и стоило, все равно пришлось бы написать грязный обновлятор Винформс, который бы подгибал элементы управления.
Re: MVU F# как избежать утечки памяти
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.10.23 12:59
Оценка: 3 (1) +1
Здравствуйте, Разраб, Вы писали:

Р>Есть вот такой код.

Р>функция view каждый раз возвращает новый элемент управления, т.к. чистая.
Р>Получается ФП не работает с Winforms? Нужны элементы ООП, чтобы избежать дикого оверхеда?

Тут есть какой-то логический разрыв. Почему не работает-то?
Вот, в своё время в WinCtl32 какой-то из контролов часть свойств давал задавать только в момент создания. Соответствующая (вполне себе ООП-шная) обёртка в Delphi изящно обошла это ограничение — когда соответствующее свойство компонента менялось, под капотом на лету пересоздавался элемент управления. Я это хорошо помню, т.к. убил довольно много времени в пошаговой отладке, пытаясь понять, какой магией заменяется хэндл окна.

То есть,
а) ООП не означает обязательного сохранения элемента управления
б) создание нового элемента управления вовсе не так уж и плохо.
б1) как минимум, утечка памяти не является неотъемлемым свойством такого подхода.

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

То, что пользовательский код написан в терминах "замены элемента управления", не требует от инфраструктурного кода слепо делать именно это.
Можно динамически определять, можно ли обойтись "перенастройкой" существующего элемента управления, или всё же нужно прямо создавать прямо новый с нуля.
И уж если всё же потребовалось создать новый, никто не мешает инфраструктуре аккуратно прибить старый, избегая утечки памяти.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: MVU F# как избежать утечки памяти
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 18.10.23 10:23
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Тут есть какой-то логический разрыв. Почему не работает-то?

S>Вот, в своё время в WinCtl32 какой-то из контролов часть свойств давал задавать только в момент создания. Соответствующая (вполне себе ООП-шная) обёртка в Delphi изящно обошла это ограничение — когда соответствующее свойство компонента менялось, под капотом на лету пересоздавался элемент управления. Я это хорошо помню, т.к. убил довольно много времени в пошаговой отладке, пытаясь понять, какой магией заменяется хэндл окна.

Да помню там для функции вызова нужно было передавать статическую функцию. А в Delphi все было на объектах. Они передавали ссылку на память динамически созданную в которой self записывался в регистр и вызывалась реальная функция объекта.
и солнце б утром не вставало, когда бы не было меня
Отредактировано 18.10.2023 11:54 Serginio1 . Предыдущая версия .
Re[3]: MVU F# как избежать утечки памяти
От: Sinclair Россия https://github.com/evilguest/
Дата: 18.10.23 13:07
Оценка:
Здравствуйте, Serginio1, Вы писали:
S> Да помню там для функции вызова нужно было передавать статическую функцию. А в Delphi все было на объектах. Они передавали ссылку на память динамически созданную в которой self записывался в регистр и вызывалась реальная функция объекта.
Очень, очень вряд ли. В более-менее все места WinAPI, где используются callback-и, есть возможность передать пользовательский параметр, и его передадут при обратном вызове.
Поэтому примерно все ООП-шные библиотеки туда передают адрес объекта. Callback обрабатывается статической функцией, которая достаёт из аргумента адрес объекта, и уже на нём вызывает виртуальный метод.
Но я, конечно же, всего знать не могу, потому было бы интересно на такое посмотреть. Особенно с учётом NX-флагов и прочей современщины.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: MVU F# как избежать утечки памяти
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 18.10.23 13:20
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>> Да помню там для функции вызова нужно было передавать статическую функцию. А в Delphi все было на объектах. Они передавали ссылку на память динамически созданную в которой self записывался в регистр и вызывалась реальная функция объекта.

S>Очень, очень вряд ли. В более-менее все места WinAPI, где используются callback-и, есть возможность передать пользовательский параметр, и его передадут при обратном вызове.
S>Поэтому примерно все ООП-шные библиотеки туда передают адрес объекта. Callback обрабатывается статической функцией, которая достаёт из аргумента адрес объекта, и уже на нём вызывает виртуальный метод.
S>Но я, конечно же, всего знать не могу, потому было бы интересно на такое посмотреть. Особенно с учётом NX-флагов и прочей современщины.
Ну возможно и апи разные. Я то еще в конце 20 века это все анализировал. При чем они память создавали и как то её еще и помечали.

Они так же и для Com интерфейсов делали c корректировкой на реальные методы
и солнце б утром не вставало, когда бы не было меня
Отредактировано 18.10.2023 13:26 Serginio1 . Предыдущая версия .
Re: MVU F# как избежать утечки памяти
От: VladD2 Российская Империя www.nemerle.org
Дата: 19.10.23 09:54
Оценка: +1
Здравствуйте, Разраб, Вы писали:

Р>Получается ФП не работает с Winforms? Нужны элементы ООП, чтобы избежать дикого оверхеда?


Вообще GUI и FP не особо хорошо работают вместе. GUI традиционно лучше ложится на ООП и событийную модель. GUI подразумевает изменяемое состояние. ФП — наоборот, трансформацию состояний чистыми функциями.

Чтобы было удобно программировать в ФП-стиле нужно и специально разрабатывать библиотеки. Можно создать GUI-либы для ФП стиля. Но смысла в этом не много. Зато ФП можно использовать в кишках.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.