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

时间:2017-03-22 21:21:36

标签: elm

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

type Msg
  = Sort (Product -> Float)

但我无法定义它:

type Msg
  = Sort (Product -> comparable)

错误说:

类型Msg必须声明其类型变量的可比性......

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

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:10)

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

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

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

type Msg
    = Sort (Product -> Product -> Order)

现在我们不必向Msg添加类型参数。但是,我们如何指定要排序的Product哪个字段?我们可以使用currying。以下是:

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

comparing : (a -> comparable) -> a -> a -> Order
comparing f x y =
    compare (f x) (f y)

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

onClick (Sort (comparing .name))

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

case msg of
    Sort comparer ->
        { model | products = List.sortWith comparer model.products } ! []

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

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

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

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

type Msg
    = Sort (Comparer Product)

为了更进一步说明这一切是如何连接的,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

答案 1 :(得分:1)

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

type Msg a = Sort (Product -> a)

您在comparable

回答了listSeq = [ [134, None, datetime.datetime(2016, 2, 12, 0, 0)], [135, None, datetime.datetime(2016, 2, 12, 0, 0)], [136, None, datetime.datetime(2016, 2, 12, 0, 0)], [138, 1, datetime.datetime(2016, 2, 12, 0, 0)], [139, None, datetime.datetime(2016, 2, 12, 0, 0)], [140, None, datetime.datetime(2016, 2, 12, 0, 0)], [141, None, datetime.datetime(2016, 2, 12, 0, 0)], [142, 3, datetime.datetime(2016, 2, 12, 0, 0)], [144, None, datetime.datetime(2016, 2, 12, 0, 0)], [145, 2, datetime.datetime(2016, 2, 12, 0, 0)] ] listSeq[:] = [x for x in listSeq if x[1] is not None] 提出的问题