我可以像这样定义标记的联合类型:
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)