给出以下类型定义
CookieManager cookieManager = CookieManager.getInstance();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
cookieManager.setAcceptThirdPartyCookies(mWebview, true);
} else {
cookieManager.setAcceptCookie(true);
}
此newtype Constant a b = Constant { getConstant :: a }
deriving (Eq, Show)
Functor
定义有效
instance
而明显等价的instance Functor (Constant a) where
fmap _ (Constant x) = Constant x
定义
instance
因类型检查错误(摘录)而失败
instance Functor (Constant a) where
fmap _ x = x
使用GHC 8.0.2版。
问题是,为什么这两个(显然是等效的)Expected type: Constant a b
Actual type: Constant a a1
定义在类型检查方面表现不同。
答案 0 :(得分:5)
如果我们给构造函数一个不同的名称,以便区分类型级别和值级别,可能会更清楚:
newtype Constant a b = ConstVal { getConstVal :: a }
deriving (Eq, Show)
instance Functor (Constant a) where
fmap _ (ConstVal x) = ConstVal x
现在,为什么你不能写fmap _ x = x
?
ConstVal
是一个多态构造函数:
ConstVal :: a -> Constant a b
...即
ConstVal :: ∀ a b . a -> Constant a b
虽然这个通用量词在Haskell中是可选的,但它实际上很重要。 ConstVal
基本上有两个额外的类型级参数。换句话说,这不只是一个构造函数,而是一整个构造函数家族,比如
ConstValBoolBool :: Bool -> Constant Bool Bool
ConstValBoolInt :: Bool -> Constant Bool Int
ConstValBoolChar :: Bool -> Constant Bool Char
...
ConstValCharBool :: Char -> Constant Char Bool
ConstValCharInt :: Char -> Constant Char Int
ConstValCharChar :: Char -> Constant Char Char
...
...
所有这些实际上共享相同的值级别名称ConstVal
,但对于类型系统,它们都是不同的。明确写出来,你有例如
fmapBoolStringInt :: (String -> Int) -> Constant Bool String -> Constant Bool Int
fmapBoolStringInt _ (ConstValBoolString x) = ConstValBoolInt x
这里显然双方的价值实际上并不相同,因此不能减少到fmapBoolStringInt _ x = x
。
答案 1 :(得分:2)
在instance Functor (Constant a)
fmap
中有类型:
fmap :: (b -> c) -> Constant a b -> Constant a c
在表达式fmap _ x = x
中,变量x
必须具有不可能的不同类型:
fmap _ (x :: Constant a b) = (x :: Constant a c)
那个typechecker说你。
您可以使用unsafeCoerce
,如下所示:
fmap _ x = unsafeCoerce x
或
fmap _ = unsafeCoerce
但这不是惯用的方式,因为即使您知道此时unsafeCoerce
的应用是安全的,您也不能保证(通常情况下)一段时间之后。
但是,自base-4.7.0.0
(coerce
在他的回答中写道)@Ben之后就有Qt/QML application的安全版本。
此外,我们可以预期编译器可以优化第一个定义,如第二个。
答案 2 :(得分:1)
fmap :: (a -> b) -> Constant t a -> Constant t b
fmap _ (Constant x) = Constant x
在这里,您解构类型Constant t a
的值以获取类型x
的值t
,然后将其包装在Constant
构造函数中以生成类型的值Constant t b
所以你可以退货。
fmap :: (a -> b) -> Constant t a -> Constant t b
fmap _ x = x
您会收到Constant t a
类型的值,并将其作为Constant t b
类型的值返回。这不起作用。
我们可以想象它可以工作。由于Constant
的第二个类型参数是一个幻像参数,因此这两种类型的内存中表示是相同的,因此对一种类型的内存中引用可以作为内存中的引用传递给另一种类型。类型,一切都会正常。
GHC运行时实际上实际上是这样做的 - x :: t
,Constant x :: Constant t a
和Constant x :: Constant t b
在运行时都将引用同一块内存,并且没有工作通过这个功能。
但这是一个低级别的优化;从语义上讲,它们仍然是不同的类型,Haskell不允许将一种类型的值用作不同类型的值。
您可以做的事情,因为Constant
是newtype
,使用coerce
(请参阅https://hackage.haskell.org/package/base/docs/Data-Coerce.html)进行明确转换。这避免了你必须明确模式匹配和重建。转换在运行时将是无操作(由于内存表示相同)。所以:
fmap _ x = coerce x
甚至:
fmap _ = coerce