我该如何解决这些类型的谜团?

时间:2016-01-11 06:35:03

标签: haskell

见这两个问题:

--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?看似奇怪,因为已知fooRealFloat2Num,这是RealFloat的祖先所以我认为2将是RealFloat能够像通常那样充当foo :: Double

我可以通过foo :: RealFloat a => a代替default ()来解决错误。但是你已经在Double看到了我的想法。我不希望在我的代码库中深入具体RealFloat。我想继续在任何地方使用main,以便我可以在Float中指定我想要的准确度。这可能是数字包中的DoubleBigFloat甚至是main

简而言之,我不想在我的代码中深入指定计算精度。准确性应保持一般,并在Paypal中指定,我要求Haskell计算事物。

有没有摆脱这种情况的方法?

2 个答案:

答案 0 :(得分:5)

如果您将多态值(例如ffoo)视为必须应用于函数,这将有助于了解正在发生的情况在进行任何计算之前输入参数

(事实上,在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