简体   繁体   English

输入num的签名加倍?

[英]Type signature of num to double?

I'm just starting Learn You a Haskell for Great Good , and I'm having a bit of trouble with type classes. 我刚刚开始学习Haskell for Great Good ,我在类型类方面遇到了一些麻烦。 I would like to create a function that takes any number type and forces it to be a double. 我想创建一个采用任何数字类型的函数,并强制它为double。

My first thought was to define 我的第一个想法是定义

numToDouble :: Num -> Double

But I don't think that worked because Num isn't a type , it's a typeclass (which seems to me to be a set of types). 但我认为这不起作用,因为Num不是一个类型 ,它是一个类型类 (在我看来它是一组类型)。 So looking at read , shows (Read a) => String -> a . 所以在看read ,表演(Read a) => String -> a I'm reading that as "read takes a string, and returns a thing of type a which is specified by the user". 我正在阅读它,因为“read接受一个字符串,并返回一个由用户指定的类型a a的东西”。 So I wrote the following 所以我写了以下内容

numToDouble :: (Num n) => n -> Double
numToDouble i = ((i) :: Double)

Which looks to me like "take thing of type n (must be in the Num typeclass, and convert it to a Double". This seems reasonable becuase I can do 20::Double 这对我来说就像“接受类型为n的东西(必须在Num类型类中,并将其转换为Double”。这似乎是合理的,因为我可以做20::Double

This produces the following output 这会产生以下输出

Could not deduce (n ~ Double)                                                                                                                                                                                                                                              
from the context (Num n)                                                                                                                                                                                                                                                   
  bound by the type signature for numToDouble :: Num n => n -> Double

I have no idea what I'm reading. 我不知道我在读什么。 Based on what I can find, it seems like this has something to do with polymorphism? 基于我能找到的,似乎这与多态性有关?

Edit: 编辑:

To be clear, my question is: Why isn't this working? 要明确的是,我的问题是:为什么这不起作用?

The reason you can say "20::Double" is that in Haskell an integer literal has type "Num a => a", meaning it can be any numeric type you like. 您可以说“20 :: Double”的原因是在Haskell中,整数文字的类型为“Num a => a”,这意味着它可以是您喜欢的任何数字类型。

You are correct that a typeclass is a set of types. 你是对的,类型类是一组类型。 To be precise, it is the set of types that implement the functions in the "where" clause of the typeclass. 确切地说,它是在类型类的“where”子句中实现函数的一组类型。 Your type signature for your numToDouble correctly expresses what you want to do. numToDouble的类型签名正确表达了您要执行的操作。

All you know about a value of type "n" in your function is that it implements the Num interface. 您在函数中对“n”类型值的所有了解都是它实现了Num接口。 This consists of +, -, *, negate, abs, signum and fromInteger. 它由+, - ,*,negate,abs,signum和fromInteger组成。 The last is the only one that does type conversion, but its not any use for what you want. 最后一个是唯一一个进行类型转换,但它没有任何用途你想要的。

Bear in mind that Complex is also an instance of Num. 请记住,Complex也是Num的一个实例。 What should numToDouble do with that? numToDouble应该怎么做? The Right Thing is not obvious, which is part of the reason you are having problems. 正确的事情并不明显,这是你遇到问题的部分原因。

However lower down the type hierarchy you have the Real typeclass, which has instances for all the more straightforward numerical types you probably want to work with, like floats, doubles and the various types of integers. 但是,在类型层次结构中,您可以使用Real类型类,它具有您可能想要使用的所有更简单的数字类型的实例,例如浮点数,双精度数和各种类型的整数。 That includes a function "toRational" which converts any real value into a ratio, from which you can convert it to a Double using "fromRational", which is a function of the "Fractional" typeclass. 这包括一个“toRational”函数,它将任何实际值转换为比率,您可以使用“fromRational”将其转换为Double,这是“Fractional”类型类的函数。

So try: 所以尝试:

toDouble :: (Real n) => n -> Double
toDouble = fromRational . toRational

But of course this is actually too specific. 但当然这实际上太具体了。 GHCI says: GHCI说:

Prelude> :type fromRational . toRational
fromRational . toRational :: (Fractional c, Real a) => a -> c

So it converts any real type to any Fractional type (the latter covers anything that can do division, including things that are not instances of Real, like Complex) When messing around with numeric types I keep finding myself using it as a kind of generic numerical coercion. 所以它将任何真实类型转换为任何分数类型(后者涵盖任何可以进行除法的事情,包括不是Real实例的事情,比如复杂)当弄乱数字类型时,我一直发现自己使用它作为一种通用数值强迫。

Edit: as leftaroundabout says, 编辑:左撇子说,

realToFrac = fromRational . toRational

You can't "convert" anything per se in Haskell. 你不能在Haskell中“转换”任何东西本身。 Between specific types, there may be the possibility to convert – with dedicated functions. 在特定类型之间,可能有转换的可能性 - 使用专用功能。

In your particular example, it certainly shouldn't work. 在您的特定示例中,它当然不应该工作。 Num is the class 1 of all types that can be treated as numerical types, and that have numerical values in them (at least integer ones, so here's one such conversion function fromInteger ). Num是可以被视为数值类型的所有类型的第1类,并且其中包含数值(至少是整数,所以这里是一个这样的转换函数fromInteger )。

But these types can apart from that have any other stuff in them, which oftentimes is not in the reals and can thus not be approximated by Double . 但是这些类型除了它们之外还有其他东西,它们通常不在实际中,因此不能用Double来近似。 The most obvious example is Complex . 最明显的例子是Complex

The particular class that has only real numbers in it is, suprise, called Real . 其中只有实数的特定类是令人惊讶的,称为Real What is indeed a bit strange is that its method is a conversion toRational , since the rationals don't quite cover the reals... but they're dense within them, so it's kind of ok. 确实有点奇怪的是,它的方法是转换为toRational ,因为理性并不能完全覆盖实数......但它们内部密集,所以它很好。 At any rate, you can use that function to implement your desired conversion: 无论如何,您可以使用该功能实现所需的转换:

realToDouble :: Real n => n -> Double
realToDouble i = fromRational $ toRational i

Incidentally, that combination fromRational . toRational 顺便提一下,这种组合fromRational . toRational fromRational . toRational is already a standard function: realToFrac , a bit more general. fromRational . toRational已经是一个标准函数: realToFrac ,更通用一点。


Calling type classes "sets of types" is kind of ok, much like you can often get away without calling any kind of collection in maths a set – but it's not really correct. 调用类型类“类型集”是很好的,就像你经常可以在没有调用数学集中的任何类型集合的情况下离开 - 但它并不是真的正确。 The most problematic thing is, you can't really say some type is not in a particular class: type classes are open, so at any place in a project you could declare an instance for some type to a given class. 最有问题的是,你不能说某些类型不在特定的类中:类型类是开放的,因此在项目的任何地方你都可以为某个类声明一个类型的实例。

There is no such thing as numeric casts in Haskell. 在Haskell中没有数字转换这样的东西。 When you write i :: Double , what that means isn't "cast i to Double "; 当你写i :: Double ,这意味着不是“把iDouble ”; it's just an assertion that i 's type is Double . 这只是一个断言, i的类型是Double In your case, however, your function's signature also asserts that i 's type is Num n => n , ie, any type n (chosen by the caller) that implements Num ; 但是,在你的情况下,你的函数的签名也断言i的类型是Num n => n ,即任何类型n (由调用者选择)实现Num ; so for example, n could be Integer . 例如, n可以是Integer Those two assertions cannot be simultaneously true, hence you get an error. 这两个断言不能同时为真,因此会出错。

The confusing thing is that you can say 1 :: Double . 令人困惑的是你可以1 :: Double But that's because in Haskell, a numeric literal like 1 has the same meaning as fromInteger one , where one :: Integer is the Integer whose value is one. 但那是因为在Haskell中,像1这样的数字文字具有与fromInteger one相同的含义,其中one :: Integer是值为1的Integer

But that only works for numeric literals. 但这只适用于数字文字。 This is one of the surprising things if you come to Haskell from almost any other language. 如果你从几乎任何其他语言来到Haskell,这是令人惊讶的事情之一。 In most languages you can use expressions of mixed numeric types rather freely and rely on implicit coercions to "do what I mean"; 在大多数语言中,您可以相当自由地使用混合数字类型的表达式,并依靠隐式强制来“做我的意思”; in Haskell, on the other hand you have to use functions like fromIntegral or fromRational all the time. 另一方面,在Haskell中,你必须fromRational使用fromIntegralfromRational等函数。 And while most statically typed languages have a syntax for casting from one numeric type to another, in Haskell you just use a function. 虽然大多数静态类型语言都有从一种数字类型转换为另一种数字类型的语法,但在Haskell中你只需使用一个函数。

Just to be 100% clear, the problem is 只是要100%清楚,问题是

(i) :: Double

This does not convert i to a Double , it demands that i already is a Double . 这不会 i 转换Double ,它要求 i 已经是 Double That isn't what you mean at all. 那不是你的意思。

The type signature for your function is correct. 您的函数的类型签名是正确的。 (Or at least, it means exactly what you think it means.) But your function's implementation is wrong. (或者至少,它意味着你认为它的意思。)但是你的函数的实现是错误的。

If you want to convert one type of data to another, you have to actually call a function of some sort. 如果要将一种类型的数据转换为另一种类型,则必须实际调用某种类型的函数

Unfortunately, Num itself only allows you to convert an Integer to any Num instance. 不幸的是, Num本身只允许您将Integer转换为任何Num实例。 You're trying to convert something that isn't necessarily an Integer , so this doesn't help. 你试图转换一些不一定是Integer东西,所以这没有帮助。 As others have said, you probably want fromRational or similar... 正如其他人所说,你可能想要fromRational或类似......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM