见这两个问题:
--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
。
简而言之,我不想在我的代码中深入指定计算精度。准确性应保持一般,并在Paypal
中指定,我要求Haskell计算事物。
有没有摆脱这种情况的方法?
答案 0 :(得分:5)
如果您将多态值(例如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
在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
答案 1 :(得分:3)
当函数签名没有完全规定内部值的类型时,例如MainActivity
public class MainActivity extends FragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/** Getting a reference to the ViewPager defined the layout file */
ViewPager pager = (ViewPager) findViewById(R.id.pager);
/** Getting fragment manager */
FragmentManager fm = getSupportFragmentManager();
/** Instantiating FragmentPagerAdapter */
MyFragmentPagerAdapter pagerAdapter = new MyFragmentPagerAdapter(fm);
/** Setting the pagerAdapter to the pager object */
pager.setAdapter(pagerAdapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
,必须在本地通知编译器有关foo和2.0的类型。这可以通过类型签名显式完成,也可以通过默认和类型推断隐式完成。
为什么呢?那么在该表达式中没有任何其他机制来键入foo
。如果你愿意,你可以自己提供机器:
foo
现在,“精确”(正如你所说的那样,并不是一个准确的术语)是被调用者通过传入正确类型来控制的东西:
import Data.Proxy
g :: RealFloat a => Proxy a -> Bool
g ty = let foo = undefined `asProxyTypeOf` ty
in foo < 2.0