Haskell类型强制

one*_*elf 2 haskell

我试图围绕Haskell式强制进行包裹.意思是,何时可以将值传递给函数而不进行强制转换以及如何工作.这是一个具体的例子,但我正在寻找一个更普遍的解释,我可以使用它来尝试理解发生了什么:

Prelude> 3 * 20 / 4
15.0
Prelude> let c = 20
Prelude> :t c
c :: Integer
Prelude> 3 * c / 4

<interactive>:94:7:
    No instance for (Fractional Integer)
      arising from a use of `/'
    Possible fix: add an instance declaration for (Fractional Integer)
    In the expression: 3 * c / 4
    In an equation for `it': it = 3 * c / 4
Run Code Online (Sandbox Code Playgroud)

(/)的类型是分数a => a - > a - > a.所以,我猜测当我使用文字做"3*20"时,Haskell会以某种方式假设该表达式的结果是一个Fractional.但是,当使用变量时,它的类型根据赋值预定义为Integer.

我的第一个问题是如何解决这个问题.我是否需要转换表达式或以某种方式转换它?我的第二个问题是,对我来说这似乎很奇怪,你不能做基本的数学而不必担心int/float类型.我的意思是有一种明显的方法可以在这些之间自动转换,为什么我不得不考虑这个并处理它?我开始做错了吗?

我基本上是在寻找一种方法来轻松编写简单的算术表达式,而不必担心整洁的细节,并保持代码的美观和干净.在大多数顶级语言中,编译器对我有用 - 而不是相反.

kos*_*kus 13

如果您只是想要解决方案,请查看结尾.

你几乎已经回答了你自己的问题.Haskell中的文字超载:

Prelude> :t 3
3 :: Num a => a
Run Code Online (Sandbox Code Playgroud)

既然(*)也有Num约束

Prelude> :t (*)
(*) :: Num a => a -> a -> a
Run Code Online (Sandbox Code Playgroud)

这延伸到产品:

Prelude> :t 3 * 20
3 * 20 :: Num a => a
Run Code Online (Sandbox Code Playgroud)

因此,根据上下文,这可以专门为类型Int,Integer,Float,Double,Rational和更多的,根据需要.特别是,作为Fractional子类Num,它可以在一个除法中使用而没有问题,但是然后约束将变得更强并且适用于类Fractional:

Prelude> :t 3 * 20 / 4
3 * 20 / 4 :: Fractional a => a
Run Code Online (Sandbox Code Playgroud)

最大的区别是标识符c是一个Integer.GHCi提示中的简单let-binding未分配重载类型的原因是可怕的单态限制.简而言之:如果您定义一个没有任何显式参数的值,那么除非您提供显式类型签名,否则它不能具有重载类型.然后将数字类型默认为Integer.

一旦c是a Integer,乘法的结果Integer也是:

Prelude> :t 3 * c
3 * c :: Integer
Run Code Online (Sandbox Code Playgroud)

而且Integer是不是在Fractional类.

这个问题有两种解决方案.

  1. 确保您的标识符也有重载类型.在这种情况下,它就像说一样简单

      Prelude> let c :: Num a => a; c = 20
      Prelude> :t c
      c :: Num a => a
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用fromIntegral铸造整数值为任意数值:

      Prelude> :t fromIntegral
      fromIntegral :: (Integral a, Num b) => a -> b
      Prelude> let c = 20
      Prelude> :t c
      c :: Integer
      Prelude> :t fromIntegral c
      fromIntegral c :: Num b => b
      Prelude> 3 * fromIntegral c / 4
      15.0
    
    Run Code Online (Sandbox Code Playgroud)


Ben*_*Ben 5

当您将一种类型传递给函数时,Haskell 永远不会自动将一种类型转换为另一种类型.它已经与预期的类型兼容,在这种情况下不需要强制,或程序无法编译.

如果你编写一个完整的程序并编译它,那么事情通常"只是工作"而你不必过多考虑int/float类型; 只要你是一致的(即你不试图将某个东西视为一个地方的Int和另一个地方的浮动),约束只会流经程序并为你找出类型.

例如,如果我将它放在源文件中并编译它:

main = do
    let c = 20
    let it = 3 * c / 4
    print it
Run Code Online (Sandbox Code Playgroud)

然后一切都很好,并运行程序打印15.0.你可以看出.0GHC成功地发现c必须是某种小数,并使一切正常,而不必给出任何明确的类型签名.

c不能是整数,因为/运算符是用于数学除法的,它不是在整数上定义的.整数除法的运算由div函数表示(以运算符方式使用x `div` y).我想这可能是你整个计划中的绊倒?遗憾的是,如果你已经习惯了许多其他语言中的情况,/有时候数学划分,有时甚至是整数除法,那么你必须通过它来学习它.

当你在解释器中玩弄事情变得混乱时,因为你倾向于绑定没有任何上下文的值.在翻译中,GHCi必须自己执行let c = 20,因为你还没有进入3 * c / 4.它没有知道你是否打算说的方式20是一个Int,Integer,Float,Double,Rational,等

Haskell将为数值选择默认类型; 否则,如果你从不使用任何仅适用于某种特定类型数字的函数,那么你总会得到关于模糊类型变量的错误.这通常可以正常工作,因为这些默认规则是在读取整个模块时应用的,因此考虑了该类型的所有其他约束(比如您是否曾经使用它/).但这里没有其他的约束可以看到,这样的类型违约挑选第一驾驶室掉等级,使cInteger.

然后,当你要求GHCi进行评估时3 * c / 4,为时已晚.c是一个Integer,所以必须3 * c,并且Integer不支持/.

所以在解释器中,是的,有时如果你没有为let绑定提供一个显式类型,GHC会选择一个不正确的类型,特别是对于数字类型.在那之后,你坚持使用GHCi选择的具体类型支持的任何操作,但是当你遇到这种错误时,你总是可以重新绑定变量; 例如let c = 20.0.

但是我怀疑在你真正的程序中,问题只是你想要的操作实际上div而不是/.