【问题标题】:How to communicate with a polymorphic child component in Elm?如何与 Elm 中的多态子组件进行通信?
【发布时间】:2018-07-05 20:34:19
【问题描述】:

我的主程序有一个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 的每个函数都需要以某种方式进行更改以使用新的子组件。

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

【问题讨论】:

  • 您能否更深入地了解您要完成的工作?孩子的目的是什么?可能有一种不同的方法,而不是在对象图中冒泡一个未确定的类型参数。
  • 为 Wordpress 制作 Elm 前端 - 在此过程中遇到了一些有趣的挑战...这里有一个 link 向您展示我在说什么。我不能把所有的都贴在 Ellie 上,因为它只允许一个文件,但我认为它足以得到图片。我考虑过将所有这些都带入主要的App,但我希望将与 Wordpress 后端相关的内容保留在一个独立的模块中。我是基于类型的编程的新手,所以我可能正在做一些愚蠢的事情......
  • 这是最近几个月努力让新手停止思考“组件”的事情之一,而 Elm 并没有真正做到这一点。查看“文件的生命周期”youtube.com/watch?v=XpDsk374LDE 或“缩放 Elm 应用程序”youtube.com/watch?v=DoA4Txr4GUs 以获得更好的理论基础。我仍然一直在制作类似于“子组件”的应用程序,但我尝试尽可能长时间地使用扁平结构,然后在它变得太大并且有太多不相关时重构功能相同的更新功能。
  • @wmakley 感谢分享。实际上,我上周观看了这两个视频,并完全根据我学到的知识重构了这个模块。没有更多的子组件:D

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


【解决方案1】:

我认为问题在于您在公开的Msg 类型中泄露了太多信息。您对Msg a 类型参数的使用似乎仅限于一组已知类型,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)

【讨论】:

  • 这看起来好多了,但是如何从 Http 命令构造这些消息?
  • 类似decoder |> Http.get url |> Http.toTask |> Task.attempt (LoadInfo worker url) ?
  • 这里只是从臀部拍摄,但我认为这应该可行:Http.send (Load << LoadInfo worker url)
  • 它的力量让我大吃一惊。我将看看如何使用这种技术改进我的程序的其他方面。谢谢,乍得。
  • 对于那些从Cmd.map 角度理解它的人,甚至是Http.send (LoadInfo worker url) |> Cmd.map LoadPost。函数式编程很重要。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-23
  • 2018-04-08
  • 2017-07-09
  • 2018-04-18
  • 2018-06-30
  • 1970-01-01
  • 2017-02-15
相关资源
最近更新 更多