`coerce`和类型变量的实例化

时间:2017-09-04 07:36:33

标签: haskell unification coerce

考虑以下GHCi会议:

>:set -XTypeApplications
>import Data.Map.Strict
>import GHC.Exts
>newtype MySet a = MySet (Map a ())
>let member' :: Ord a => a -> MySet a -> Bool; member' = coerce member

<interactive>:21:57: error:
    * Couldn't match representation of type `a0' with that of `()'
        arising from a use of `coerce'
    * In the expression: coerce member
      In an equation for member': member' = coerce member
>let member' :: Ord a => a -> MySet a -> Bool; member' = coerce (member @_ @())

我对这里发生的事情有了预感:类型检查器需要满足Coercible (Ord a => a -> Map a b -> Bool) (Ord a => a -> MySet a -> Bool)并且无法在此约束中将b实例化为{{1} }}

使用()是否有更优雅的方式?

编辑:我特别在寻找能够解决类型-XTypeApplications中出现的很多问题的解决方案,例如MySet a

2 个答案:

答案 0 :(得分:2)

member :: Ord a => a -> Map a b -> Bool
member' :: Ord a => a -> MySet a -> Bool

GHC需要接受

Coercible (Map a b) (MySet a)

它看到了

Coercible (MySet a) (Map a ())

让它需要

Coercible (Map a ()) (Map a b)

导致

Coercible () b

但是b是什么?这是模棱两可的。在这种情况下,b是什么并不重要,因为通过参数化,member无法关注。因此,选择b ~ ()并轻松解决胁迫是完全合理的。但是GHC通常不会在类型推断中执行这样的参数分析。我怀疑改变它可能会很棘手。最特别的是,任何时候类型推断“猜测”,它都有可能猜错的风险并阻塞其他地方的推理。这是一大堆蠕虫。

至于你的问题,我没有一个好的解决方案。当你有几个具有类似模式的函数时,你可以将它们抽象出来,但你仍然会遇到很大的烦恼。

答案 1 :(得分:1)

TypeApplications的解决方案非常简单:

{-# LANGUAGE TypeApplications #-}

import Data.Coerce
import qualified Data.Map as M

newtype Set a = Set (M.Map a ())

member :: Ord a => a -> Set a -> Bool
member = coerce (M.member @_ @())

union :: Ord a => Set a -> Set a -> Set a
union = coerce (M.union @_ @())

请注意,某些功能需要更多或更少的通配符,例如

smap :: (Ord b) => (a -> b) -> Set a -> Set b
smap = coerce (M.mapKeys @_ @_ @())

要确定您必须如何指定类型应用程序(除了试验和错误),请使用

>:set -fprint-explicit-foralls
>:i M.mapKeys
M.mapKeys ::
  forall k2 k1 a. Ord k2 => (k1 -> k2) -> M.Map k1 a -> M.Map k2 a

您从:i获得的变量订单与TypeApplications使用的变量订单相同。

请注意,您无法coerce使用fromList - 这不是一个限制,它只是没有意义:

fromList :: Ord a => [a] -> Set a
fromList = coerce (M.fromList @_ @())

这给出了错误

* Couldn't match representation of type `a' with that of `(a, ())'

你可以做的最好的事情可能是

fromList :: Ord a => [a] -> Set a
fromList = coerce (M.fromList @_ @()) . map (flip (,) ())