如何与Elm中的多态子组件进行通信?

时间:2018-07-05 20:34:20

标签: functional-programming polymorphism elm unification union-types

我的主程序具有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 :(得分:3)

我认为问题在于,您在公开公开的Msg类型中泄漏了太多信息。您对Msg a的type参数的使用似乎仅限于一组已知的类型,即AuthorCategoryPostTag。从略读代码来看,看起来将永远只是这四个代码之一,因此,您应将以这种方式抽象化的事实保存在该模块内部,而不是公开它并负担任何其他可能拉扯的代码这个。

我认为您需要将抽象向下移动,以避免参数化您的公共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)