gpt4 book ai didi

haskell - 当我指定 x 的类型为 a 时,为什么 Haskell 会尝试推断它的类型为 a0 ?

转载 作者:行者123 更新时间:2023-12-02 04:07:17 26 4
gpt4 key购买 nike

有时,我会在签名中指定某些内容的类型,例如 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的实例。您可能遇到的模糊类型错误与此有关(但这可能是一个棘手的错误消息,我不会责怪您只是启用了扩展)。

快速解决方法是使用 ScopedTypeVariablesTypeApplications 手动指定 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/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com