如何与 Elm 中的多态 child 组件通信?

How to communicate with a polymorphic child component in Elm?

我的主程序有一个update函数

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

要与 sub-components 通信,我们可以添加另一个变体并将我们的消息包装在新消息中

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 )

然而,如果我的 sub-component 的 update 函数的类型与 parent 不匹配,这似乎具有挑战性。考虑具有多态更新函数的 child:

-- 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 的函数都需要以某种方式进行更改以与新的 child 组件一起使用。

如何统一这两种类型并使这两个组件协同工作?

我认为问题是您在 publicly 公开的 Msg 类型中泄露了太多信息。您对 Msg a 类型参数的使用似乎仅限于一组已知类型,AuthorCategoryPostTag。从浏览你的代码来看,它似乎永远只是这四个之一,所以你以这种方式抽象事物的事实应该保留在这个模块内部,而不是暴露它并给任何其他可能拉动的代码增加负担这个在.

我认为您需要将抽象下移一个级别以避免参数化您的 public 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)