看到这两个问题:
--I don't want any defaulting. For example, I don't want --a general Num to convert to Double deep within my codebase. --I want to keep my codebase as general as possible. --Only in main, where I actually call my pure functions, --do I want to specify an actual, concrete type. default () f :: RealFloat a => a f = undefined g :: Bool g = let foo :: RealFloat a => a --First question: Why do I even need this? foo = f in foo < 2.0 --Second question: Why is this an error?
首先,为什么我需要明确地告诉Haskell类型foo
?为什么它不能从类型中自动推断出来f
?
二,为什么不foo < 2
编译?似乎很奇怪,因为foo
已知是RealFloat
和2
现在Num
,这是一个祖先,RealFloat
所以我认为2
可以RealFloat
像往常一样.
我可以通过foo :: Double
代替而来解决错误foo :: RealFloat a => a
.但你已经看到了我的想法default ()
.我不想Double
在我的代码库中深入具体.我想继续RealFloat
在任何地方使用,所以我可以指定我想要的准确度main
.那可能是Float
,Double
甚至BigFloat
是数字包.
简而言之,我不想在我的代码中深入指定计算精度.准确性应保持一般,并main
在我要求Haskell计算事物的地方指定.
有没有摆脱这种情况的方法?
如果您将多态值(例如f
或foo
)视为必须在可以执行任何计算之前应用于类型参数的函数,它将有助于理解发生了什么.
(实际上,在GHC 8.0中,您将能够在语言本身中使用此类型的应用程序.)
我先回答你的第二个问题:为什么会foo < 2.0
出错?因为foo
是多态的,必须在某种类型实例化,以便计算结果.语义<
取决于这种实例化,并且根据您选择的类型,您可能会得到不同的答案.
所以,这有效:
default () f :: RealFloat a => a f = undefined g :: Bool g = let foo = f in foo < (2.0 :: Double)
应该回答你的第一个问题,"为什么我甚至需要这个?" - 你没有.
现在,看起来你真的希望你的代码具有多态性.为此,您需要g
从外部了解用于计算的类型.你问:
为什么不能从f的类型中自动推断出它呢?
嗯,这是因为f
它也是多态的,所以它不知道它的类型本身!它也是从类型到该类型值的函数.在程序的不同部分,它可以在不同类型实例化,并评估为不同的值.
为了告诉g使用哪种类型,您可以添加如下代理参数:
{-# LANGUAGE ScopedTypeVariables #-} import Data.Proxy default () f :: RealFloat a => a f = undefined g :: forall a . RealFloat a => Proxy a -> Bool g _ = let foo = f in foo < (2.0 :: a)
绕过代理可能不方便.相反,您可以使用隐式参数:
{-# LANGUAGE ScopedTypeVariables, ImplicitParams #-} import Data.Proxy default () f :: RealFloat a => a f = undefined g :: forall a . (RealFloat a, ?t :: Proxy a) => Bool g = let foo = f in foo < (2.0 :: a)
这应该可以得到你所要求的; 你可以说
let ?t = Proxy :: Proxy Double
in main
,信息将g
自动传播.
在GHC 8.0中,您可以Proxy
通过以下方式启用来替换TypeApplications
:
{-# LANGUAGE ScopedTypeVariables, TypeApplications #-} f :: RealFloat a => a f = 2.0 - 1e-12 g :: forall a . RealFloat a => Bool g = let foo = f @a in foo < 2 main = do print $ g @Float print $ g @Double