8

考虑以下 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在此约束中实例化为().

有没有比这样做更优雅的方法-XTypeApplications

编辑:我特别在寻找处理MySet a类型中许多出现的解决方案,例如union :: Ord a => MySet a -> MySet a -> MySet a.

4

2 回答 2

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 在类型推断中一般不会进行这样的参数化分析。我怀疑改变它可能很棘手。最特别的是,任何时候类型推断“猜测”,都有可能猜错并在其他地方阻止推断。这是一大罐虫子。

至于你的问题,我没有好的解决办法。当你有几个具有相似模式的函数时,你可以将它们抽象出来,但你仍然会面临很大的烦恼。

于 2017-09-05T07:16:00.837 回答
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

您从中获得的变量顺序:iTypeApplications.

请注意,您不能使用coercefor 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 (,) ())
于 2017-09-05T17:21:37.580 回答