我刚刚开始了解你是一个很好的Haskell ,我在类型类方面遇到了一些麻烦。我想创建一个采用任何数字类型的函数,并强制它为double。
我的第一个想法是定义
numToDouble :: Num -> Double
但我认为这不起作用,因为Num
不是类型,它是类型类(在我看来它是一组类型)。因此,查看read
,会显示(Read a) => String -> a
。我正在阅读它,因为“read接受一个字符串,并返回一个由用户指定的a
类型的东西”。所以我写了以下
numToDouble :: (Num n) => n -> Double
numToDouble i = ((i) :: Double)
在我看来“喜欢n型的东西(必须在Num
类型类中,并将其转换为Double”。这似乎是合理的,因为我能做20::Double
这会产生以下输出
Could not deduce (n ~ Double)
from the context (Num n)
bound by the type signature for numToDouble :: Num n => n -> Double
我不知道我在读什么。基于我能找到的,似乎这与多态性有关?
修改
要说清楚,我的问题是:为什么这不起作用?
答案 0 :(得分:9)
您可以说“20 :: Double”的原因是在Haskell中,整数文字的类型为“Num a => a”,这意味着它可以是您喜欢的任何数字类型。
你是对的,类型类是一组类型。确切地说,它是在类型类的“where”子句中实现函数的一组类型。 numToDouble的类型签名正确表达了您想要做的事情。
您在函数中对“n”类型值的所有了解都是它实现了Num接口。它由+, - ,*,negate,abs,signum和fromInteger组成。最后一个是唯一一个进行类型转换的人,但它并没有用于你想要的东西。
请记住,Complex也是Num的一个实例。 numToDouble应该怎么做?正确的事情并不明显,这是你遇到问题的部分原因。
然而,在类型层次结构中,您可以使用Real类型类,它具有您可能想要使用的所有更简单的数字类型的实例,例如浮点数,双精度数和各种类型的整数。这包括一个“toRational”函数,它将任何实际值转换为一个比率,您可以使用“fromRational”将其转换为Double,这是“Fractional”类型类的函数。
所以试试:
toDouble :: (Real n) => n -> Double
toDouble = fromRational . toRational
但当然这实际上太具体了。 GHCI说:
Prelude> :type fromRational . toRational
fromRational . toRational :: (Fractional c, Real a) => a -> c
所以它将任何真实类型转换为任何分数类型(后者涵盖任何可以进行除法的事情,包括不是Real实例的事情,比如复杂)当弄乱数字类型时,我一直发现自己使用它作为一种类型通用数值强制。
编辑,如左下图所示,
realToFrac = fromRational . toRational
答案 1 :(得分:3)
你不能在Haskell中“转换”任何东西本身。在特定类型之间,可能有转换的可能性 - 使用专用函数。
在您的特定示例中,肯定不应该工作。 Num
是所有类型的类 1 ,可以处理作为数字类型,中包含数值(at最小整数,所以这里有一个这样的转换函数fromInteger
)。
但是这些类型可以与其他任何东西分开,这些东西通常不在实际中,因此不能用Double
来近似。最明显的例子是Complex
。
其中只有 实数的特定类,可以称为Real
。确实有点奇怪的是它的方法是一个转换toRational
,因为理性并没有完全覆盖实数...但它们在它们内部是密集的,所以它是好的。无论如何,您可以使用该功能实现所需的转换:
realToDouble :: Real n => n -> Double
realToDouble i = fromRational $ toRational i
顺便提一下,该组合fromRational . toRational
已经是一个标准功能:realToFrac
,更为通用。
调用类型类“类型集”是很好的,就像你经常可以在没有调用数学集中的任何类型集合的情况下逃脱 - 但它并不是真的正确。最有问题的是,你不能说特定类中某些类型不:类型类是开放的,所以在项目的任何地方你都可以将某个类型的实例声明为给定的课程。
答案 2 :(得分:3)
Haskell中没有数字转换。当你写i :: Double
时,这意味着不是“将i
强制转换为Double
”;这只是一个断言i
的类型是Double
。但是,在您的情况下,您的函数的签名还断言i
的类型为Num n => n
,即实现n
的任何类型Num
(由调用者选择);例如,n
可以是Integer
。这两个断言不能同时成立,因此会出错。
令人困惑的是,你可以说1 :: Double
。但那是因为在Haskell中,像1
这样的数字文字与fromInteger one
具有相同的含义,其中one :: Integer
是Integer
,其值为1。
但这仅适用于数字文字。如果你从几乎任何其他语言来到Haskell,这是令人惊讶的事情之一。在大多数语言中,您可以相当自由地使用混合数字类型的表达式,并依靠隐式强制来“做我的意思”;另一方面,在Haskell中,您必须始终使用fromIntegral
或fromRational
等函数。虽然大多数静态类型语言都有从一种数字类型转换为另一种数字类型的语法,但在Haskell中你只需使用一个函数。
答案 3 :(得分:3)
要100%明确,问题是
(i) :: Double
这不将 i
转换为Double
,要求 i
已经 > a Double
。这根本不是你的意思。
您的功能的类型签名是正确的。 (或者至少,它意味着你认为它的意思。)但是你的函数的实现是错误的。
如果您想将一种类型的数据转换为另一种类型,您必须实际调用某种类型的函数。
不幸的是,Num
本身只允许您将Integer
转换为任何Num
实例。你试图转换一些不一定是Integer
的东西,所以这没有帮助。正如其他人所说,你可能想要fromRational
或类似......