在Elm中,如何在标记的联合类型中使用可比类型?

Nan*_* Li 2 elm

我可以像这样定义标记的联合类型:

type Msg
  = Sort (Product -> Float)
Run Code Online (Sandbox Code Playgroud)

但我无法定义它:

type Msg
  = Sort (Product -> comparable)
Run Code Online (Sandbox Code Playgroud)

错误说:

类型Msg必须声明其使用类型变量可比较...

但可比较的是预定义的类型变量,对吧?

我该如何解决?

Cha*_*ert 10

这个问题有点像XY问题.我想提供一种不同的思考方式来考虑在你的消息中传递排序函数(需要注意的是我不熟悉你的代码库,只有你在问题中给出的例子).

添加一个类型参数Msg确实看起来有点乱,所以让我们退后一步.排序涉及以某种方式比较两个相同类型并返回第一个值是小于,等于还是大于第二个值.Elm已经有一个Order类型用于比较具有类型构造函数LT,EQ和GT的东西(对于Less Than,EQual和Greater Than).

让我们重构你Msg的以下内容:

type Msg
    = Sort (Product -> Product -> Order)
Run Code Online (Sandbox Code Playgroud)

现在我们不必添加类型参数Msg.但是,我们如何指定Product要排序的字段呢?我们可以使用currying.这是如何做:

让我们定义另一个函数comparing,该函数将函数作为其第一个参数和另外两个相同类型的参数,并返回一个Order值:

comparing : (a -> comparable) -> a -> a -> Order
comparing f x y =
    compare (f x) (f y)
Run Code Online (Sandbox Code Playgroud)

请注意,第一个参数是一个类似于您的示例尝试在构造函数的(Product -> comparable)参数中尝试的Sort函数.这不是巧合.现在,通过使用currying,我们可以部分地将该comparing函数应用于记录字段getter,如.name.price.要修改您的示例,onClick处理程序可能如下所示:

onClick (Sort (comparing .name))
Run Code Online (Sandbox Code Playgroud)

如果你走这条路,就会有更多的重构.既然你有这个比较功能,你如何在你的update功能中使用它?假设你Model有一个叫做products类型的字段List Product.在这种情况下,我们可以使用该List.sortWith函数对列表进行排序.你的更新案例Sort Msg看起来像这样:

case msg of
    Sort comparer ->
        { model | products = List.sortWith comparer model.products } ! []
Run Code Online (Sandbox Code Playgroud)

一些结束的想法和其他说明:

关于comparing函数的这项业务直接来自Haskell,它满足了同样的需求.

不是Sort像上面那样定义构造函数,我可能会将它抽象出来,因为它是如此常见的习语.您可以为这样的通用函数定义别名,然后重新定义Msg,如下所示:

type alias Comparer a =
    a -> a -> Order

type Msg
    = Sort (Comparer Product)
Run Code Online (Sandbox Code Playgroud)

并且为了说明这一切是如何连接而更进一步,以下两种类型的注释comparing是相同的:

-- this is the original example from up above
comparing : (a -> comparable) -> a -> a -> Order

-- this example substitutues the `Comparer a` alias, which may help further 
-- your understanding of how it all ties together
comparing : (a -> comparable) -> Comparer a
Run Code Online (Sandbox Code Playgroud)