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

在Elm中,如何在标记的联合类型中使用可比较类型?,elm,Elm,我可以这样定义标记的联合类型: type Msg = Sort (Product -> Float) 但我不能这样定义: type Msg = Sort (Product -> comparable) 错误显示: 类型Msg必须声明其使用类型变量comparable 但是Compariable是一个预定义的类型变量,对吗 如何修复此问题?您得到的错误是,comparable是未绑定的变量类型。您需要在右侧完全指定它(例如,Product->Int),或者在左侧指定您希望它

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

type Msg
  = Sort (Product -> Float)
但我不能这样定义:

type Msg
  = Sort (Product -> comparable)
错误显示:

类型
Msg
必须声明其使用类型变量comparable

但是Compariable是一个预定义的类型变量,对吗


如何修复此问题?

您得到的错误是,
comparable
是未绑定的变量类型。您需要在右侧完全指定它(例如,
Product->Int
),或者在左侧指定您希望它是多态的。大概是这样的:

type Msg a=Sort(Product->a)


你问的关于可比性的问题在这里得到了回答:

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

<> >将一个类型参数添加到<代码> MSG似乎有点混乱,所以让我们后退一步。排序涉及以某种方式比较两个相同类型,并返回第一个值是否小于、等于或大于第二个值。Elm已经有了一个用于比较对象的类型,该类型具有类型构造函数LT、EQ和GT(用于小于、等于和大于)

让我们将您的
Msg
重构为以下内容:

type Msg
=排序(产品->产品->订单)
现在我们不必向
Msg
添加类型参数。但是,那么,我们如何指定要排序的
产品
的哪个字段呢?我们可以用咖喱。以下是方法:

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

比较:(a->comparable)->a->a->Order
比较f×y=
比较(f x)(f y)
请注意,第一个参数是一个函数,它看起来类似于您的示例在
排序
构造函数的
(Product->comparable)
参数中尝试的函数。这不是巧合。现在,通过使用currying,我们可以将
比较
函数与记录字段getter一起部分应用,如
.name
.price
。要修改您的示例,
onClick
处理程序可以如下所示:

onClick(排序(比较.name))
如果你走这条路,将会有更多的重构。既然您有了这个比较函数,那么如何在
update
函数中使用它呢?假设您的
模型
有一个名为
产品
的字段,其类型为
列出产品
。在这种情况下,我们可以使用函数对列表进行排序。
Sort
Msg
的更新案例如下所示:

消息的大小写
排序比较器->
{model | products=List.sortWith comparer model.products}![]

一些结束语和其他注释:

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

与其像上面那样定义
Sort
构造函数,我可能会把它抽象出来一点,因为这是一种常见的习惯用法。您可以为这样的通用函数定义别名,然后重新定义
Msg
,如下所示:

键入别名比较器a=
a->a->订单
输入味精
=排序(比较器产品)
为了进一步说明这一切是如何联系在一起的,以下两种用于比较的类型注释是相同的:

——这是上面的原始示例
比较:(a->可比)->a->a->订单
--此示例替代了'Comparer a'别名,这可能有助于进一步改进
--你对这一切是如何联系在一起的理解
比较:(a->可比较)->比较器a

您是否尝试过遵循elm编译器说明?您可能需要这样更改声明:
type Msg comparable=…
谢谢。但是如果我在左边指定它,那么当我在视图中使用Msg时,我必须指定一个真正的类型,比如“view:Model->Html(Msg Float)”,它首先不能定义类型变量(我希望能够在不同的字段上排序,比如“onClick(Sort.price)”或“onClick(Sort.name)”,它将位于同一视图中,但具有不同的字段类型)。那么有可能做到这一点吗?听起来你也需要你的模型是多态的<代码>模型a={data:List a}非常感谢。这正是我想要实现的,但我不知道怎么做。我从你放在这里的信息中学到了很多。很抱歉让人觉得这是个XY问题。当我面对参数化联合类型的问题时,我觉得它比我试图解决的原始问题(传递排序函数)更重要,所以我急切地想首先找到答案。但是你是对的,似乎这个问题从整体上得到了更好的解释。在我看到你的答案之前,我还试图通过定义不同的消息类型来解决这个问题:“type Msg=SortFloat(Product->Float)| SortString(Product->String)”,然后是“onClick(SortFloat.price)”和“onClick(SortString.name)”。这是可行的,但这不是一个好的解决方案,因为我们只是为了处理两种不同的类型而重复相同的逻辑。我只是想分享一下我的尝试,但是你的答案绝对是正确的,受你的想法启发,我们可以调用List.sortBy,在视图级别使用第一个参数,因此不需要在Msg级别处理任何类型变量,“type Msg=Sort(List Product->List Product)”,然后是“onClick(Sort(List.sortBy.name)),以及“onClick(Sort(List.sortBy.price))”,更新将是“Sort f的case msg->{model | products=f model.products}”。咖喱的力量!