upsiflu / less-ui

Write your views across several screen regions, and hide all Ui state in the Url.
https://package.elm-lang.org/packages/upsiflu/less-ui/latest
BSD 3-Clause "New" or "Revised" License
6 stars 1 forks source link

Enable `map` #7

Closed upsiflu closed 1 year ago

upsiflu commented 1 year ago

Situation

Html.map is [https://sporto.github.io/elm-patterns/architecture/nested-tea.html](a widely used pattern for larger SPAs where modules have their own Msg type), as well as for mixing Ui's made with different libraries.

Possible fixes

(a) Give Ui an inner and outer type

Problem: What is the type of the wrapped Ui? In fact, Wrap only wraps a subset of the Ui, which means it needs to produce the same type anyways.

(b) Defunctionalise wrapper and make it a second type parameter

    type alias Ui html wrapper = ...

    type alias Layout html wrapper

(c) Do (b), then merge the two parameters

----Layout.elm
type alias Layout { aspect : aspect, html : html, wrapper : wrapper } =
            { markRemovals : html -> html
            , wrap : wrapper -> html -> html
            , view : ViewModel aspect html -> html 
            }

----Ui.elm
type alias Ui layout =
     List (Descendant layout)

type Descendant { layout | html, wrapper } =
      = Twig html (Maybe (Item html))
      | Wrap wrapper (Ui html)

with : aspect -> Ui { layout | aspect : aspect } -> Ui { layout | aspect : aspect } -> Ui { layout | aspect : aspect }
            ...

map : (layout -> layout2) -> Ui layout -> Ui layout2
            ...

view : { current : State, previous : Maybe State } -> 
            Layout { layout | html : html } -> 
            Ui { layout | html : html } -> 
            html
            ...

----MyApp.elm

myLayout : Layout { aspect : (), html : String, wrapper : () }
myLayout =
    { markRemovals = \_-> ""
    , wrap = \() -> String.surround "-"
    , view { handle, get } = handle ++ get ()
    }

Ui.html "Text"
    |> wrap ()
    |> Ui.view state myLayout
    --> "-Text-"
**Pros:**

- We can build targeted layouts for common Ui types such as elm-ui, Html.Keyed, Html.Keyed.lazy, HtmlString, ()
- Reduces clutter
- makes all functions in Ui have the same specificity of type (no more Ui (Html msg))
upsiflu commented 1 year ago

Update Feb 7:

Consideration

  1. What are the use cases for map?
    • I implemented key and unkey, which are very common for Html (and for tagging other types)
  2. What are the use cases for wrap?
    • Idea: mandate keyed html for wrap target, output unkeyed Ui, then key before wrapping again
    • Related: key only makes sense when we have lists where things may change position, so indexedMap would be useless. Instead, we want to process a List (String, t) into a Ui (String, html) where the author provides a view : t->Ui html.
  3. Balance:
    • Code terseness
    • Api simplicity
    • Easy composition with elm-w3, elm-widgets, elm-theme, elm-ui, elm-css & friends
    • Expressive power (through functional composition) within the scope
    • Bug prevention, testability, fewer edge cases
upsiflu commented 1 year ago

Now as everything is #16 defunctionalised, we can re-implement map on Ui. it would probably delete any item that has a function (the constructor currently called Stateful which wraps html with Ui state information).

Also check out the pattern where Html gets a payload. might be a nice meeting target and could allow defunc wrapped html.

upsiflu commented 1 year ago

Commit Ui: implement map; fix stateful