- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
有时,我会在签名中指定某些内容的类型,例如 a
,GHC 会响应说它无法推断其类型为 a0
。发生这种情况的原因是单一的,还是有多种不同的可能原因?有时能解决,有时不能;我希望有一个统一的理论。
这是一个简短的示例。 (要查看此代码(包括解释其尝试执行的操作的注释),请参阅 here 。)
{-# LANGUAGE MultiParamTypeClasses
, AllowAmbiguousTypes
, FlexibleInstances
, GADTs #-}
type SynthName = String
data Synth format where
Synth :: SynthName -> Synth format
data MessageA format where
MessageA :: String -> MessageA format
data MessageB format where
MessageB :: String -> MessageB format
class (Message format) a where
theMessage :: a -> String
instance (Message format) (MessageA format) where
theMessage (MessageA msg) = msg
instance (Message format) (MessageB format) where
theMessage (MessageB msg) = msg
play :: Message format m => Synth format -> m -> IO ()
play (Synth name) msg =
print $ name ++ " now sounds like " ++ theMessage msg
这会产生以下错误。
riddles/gadt-forget/closest-to-vivid.hs:38:42: error:
• Could not deduce (Message format0 m)
arising from a use of ‘theMessage’
from the context: Message format m
bound by the type signature for:
play :: forall format m.
Message format m =>
Synth format -> m -> IO ()
at riddles/gadt-forget/closest-to-vivid.hs:36:1-54
The type variable ‘format0’ is ambiguous
Relevant bindings include
msg :: m (bound at riddles/gadt-forget/closest-to-vivid.hs:37:19)
play :: Synth format -> m -> IO ()
(bound at riddles/gadt-forget/closest-to-vivid.hs:37:1)
These potential instances exist:
instance Message format (MessageA format)
-- Defined at riddles/gadt-forget/closest-to-vivid.hs:30:10
instance Message format (MessageB format)
-- Defined at riddles/gadt-forget/closest-to-vivid.hs:32:10
• In the second argument of ‘(++)’, namely ‘theMessage msg’
In the second argument of ‘(++)’, namely
‘" now sounds like " ++ theMessage msg’
In the second argument of ‘($)’, namely
‘name ++ " now sounds like " ++ theMessage msg’
|
38 | print $ name ++ " now sounds like " ++ theMessage msg
最佳答案
Message
是一个多参数类型类。为了确定使用哪个实例,需要对a
和格式进行具体选择。不过,方法
theMessage :: a -> String
甚至没有提到格式
,因此我们无法确定使用哪种具体类型来查找Message
的实例。您可能遇到的模糊类型错误与此有关(但这可能是一个棘手的错误消息,我不会责怪您只是启用了扩展)。
快速解决方法是使用 ScopedTypeVariables
和 TypeApplications
手动指定 format
变量(或添加代理格式
) > theMessage
的参数)。
play :: forall format m. Message format m => Synth format -> m -> IO ()
play (Synth name) msg =
print $ name ++ " now sounds like " ++ theMessage @format msg
但是,Message
类因滥用类型类而引发危险信号。这并不总是坏事,但是每当你看到一个类的方法都具有类似
:: a -> Foo
:: a -> Bar
即它们在逆变位置采用单个 a
,很可能您根本不需要类型类。将类转换为数据类型通常更清晰,如下所示:
data Message format = Message { theMessage :: String }
其中每个方法成为一个记录字段。然后,您实例化的具体类型(例如 MessageA
)将“降级”为函数:
messageA :: String -> Message format
messageA msg = Message { theMessage = msg }
每当您传递带有 Message
约束的 a
时,只需传递 Message
即可。 a
化为虚无。
进行因式分解后,您可能会注意到您所写的很多内容都是同义反复且不必要的。好的!去掉它!
关于haskell - 当我指定 x 的类型为 a 时,为什么 Haskell 会尝试推断它的类型为 a0 ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51370183/
我是一名优秀的程序员,十分优秀!