2017-09-04 117 views
8

考虑以下GHCI会议:`coerce`和类型变量的实例

>: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许多发生。

+1

'member'ks = member k(coerce s)''怎么样? –

+0

好的想法,但是对于'union'(其中'MySet'在类型中多次出现)而言,这是非常详细的,并且根本不可能用于'fromList'。 –

+2

我认为这可能是GHC中的一个相当大的错误,当然值得一票。或者在牛津与理查德交谈:-) –

回答

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

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

+0

听起来有点像我的直觉告诉我的,但我不确定。谢谢!使用'-XTypeApplications'非常繁琐,但它工作:https://github.com/sgraf812/pomaps/blob/6d077d3413c3fcc0de05955c2d6b9bd82c085a85/library/Data/Poset/Internal.hs –

+0

顺便说一句,我认为第三和第四个“强制切换。并不是说它改变了这个想法。 –

+1

@SebastianGraf,如果你包装一个完整的模块,你会发现使用完整类型签名比类型应用更容易。 'foo = coerce(M.foo :: theoldthing this that):: forall this that。这是新事物)'。以这种方式复制/粘贴更多,而不用考虑哪些类型参数在哪里。 – dfeuer

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使用的相同。

请注意,您不能使用fromListcoerce - 这不是一种限制,它只是没有任何意义:

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 (,)()) 
+1

注意:OP明确表示他们知道TypeApplication解决方案,但我认为它非常简单 - 所以这个答案本质上是“不,没有更优雅的方式”。但是,这看起来主要是基于意见的,所以相反,我将答案本身称为一种(简单!)方法,可以用这种方法机械地编写这样的代码,而不用考虑如何去做。 – user2407038

+1

':set -fprint-explicit-foralls'听起来就像我在实现https://github.com/sgraf812/pomaps/blob/6d077d3413c3fcc0de05955c2d6b9bd82c085a85/library/Data/Poset/Internal.hs时知道的那样。 。感谢您的提示,但我认为其他帖子是对问题的更直接回答。 –