Как общаться с полиморфным дочерним компонентом в Elm? - PullRequest
0 голосов
/ 05 июля 2018

Моя основная программа имеет update функцию

update : Msg -> Model -> ( Model, Cmd Msg )

Для связи с подкомпонентами мы можем добавить другой вариант и обернуть наши сообщения в новое сообщение

type alias Model =
    { ...
    , child : Child.Model
    }

type Msg
    = ...
    | ChildMsg Child.Msg

update msg model =
    case msg of
        ...

        ChildMsg childMsg ->
          let
              ( childModel, cmd ) =
                  Child.update childMsg model.child

              updatedModel =
                  { model | child = childModel }

              childCmd =
                Cmd.map ChildMsg cmd
          in
               ( updatedModel, childCmd )

Однако это кажется сложной задачей, если тип функции update моего подкомпонента не соответствует родительскому. Рассмотрим ребенка с функцией полиморфного обновления:

-- PolymorphicChild.elm

update : Msg a -> Model -> ( Model, Cmd (Msg a) )

При запуске команды из этого модуля я должен обернуть ее

PolymorphicChild.someCommand : Cmd (Msg Foo)

PolymorphicChild.someCommand
  |> Cmd.map PolymorphicChild

Однако при этом получается Msg (PolymorphicChild.Msg Foo), а не Msg PolymorphicChild.Msg, ожидаемое моим приложением.

The right side of (|>) is causing a type mismatch.

(|>) is expecting the right side to be a:

    Cmd (PolyMorphicChild.Msg Foo) -> a

But the right side is:

    Cmd Polymorphic.Msg -> Cmd Msg

Я попытался добавить полиморфный параметр к App.Msg

-- App.elm

type Msg a =
   = ..
   | PolymorphicChildMsg (PolymorphicChild.Msg a) 

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

Как я могу объединить два типа и заставить два компонента работать вместе?

1 Ответ

0 голосов
/ 06 июля 2018

Я думаю, что проблема в том, что вы пропускаете слишком много информации в вашем общедоступном типе Msg. Использование параметра типа Msg a кажется ограниченным известным набором типов: Author, Category, Post или Tag. От просмотра вашего кода, похоже, что он никогда не будет ничем иным, как одним из этих четырех, поэтому тот факт, что вы абстрагируете вещи таким способом, должен быть сохранен внутри этого модуля, а не подвергать его воздействию и обременять любой другой код, который может вытягивать это в.

Я думаю, вам нужно переместить абстракцию вниз на уровень, чтобы избежать параметризации вашего открытого типа Msg. Я бы предложил использовать четыре конкретных конструктора для Msg вместо параметризации и перенести абстракцию на вспомогательный тип LoadInfo a:

type alias LoadInfo a =
    { worker : Worker a
    , url : Url
    , result : Result Http.Error ( Int, List a )
    }

type Msg
    = LoadPost (LoadInfo Post)
    | LoadCategory (LoadInfo Category)
    | LoadTag (LoadInfo Tag)
    | LoadAuthor (LoadInfo Author)
...