`coerce` 和类型变量的实例化
`coerce` and instantiation of type variables
考虑以下 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
.
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在类型推断中一般不会进行这样的参数分析。我怀疑改变它可能很棘手。最特别的是,任何时候类型推断 "guesses",都存在它可能猜错并在其他地方阻止推断的风险。这是一大罐蠕虫。
关于你的问题,我没有很好的解决办法。当你有几个具有相似模式的功能时,你可以将它们抽象出来,但你仍然会面临很大的烦恼。
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 (,) ())
考虑以下 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
.
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在类型推断中一般不会进行这样的参数分析。我怀疑改变它可能很棘手。最特别的是,任何时候类型推断 "guesses",都存在它可能猜错并在其他地方阻止推断的风险。这是一大罐蠕虫。
关于你的问题,我没有很好的解决办法。当你有几个具有相似模式的功能时,你可以将它们抽象出来,但你仍然会面临很大的烦恼。
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 (,) ())