在oop中,例如java,当类型实际上是子类时,我们只能将超级类转发为子类。
但是在haskell中,我们可以简单地将一个类型“转发”到该类型类的任何实例中。例如返回fromInteger
的{{1}}。从我的观点来看,它实际上是一个 Int 所以它不能被“下载”到 Float ,但它可以。
Num
另一个例子是将Prelude System.Random> :t fromInteger a
fromInteger a :: Num a => a
Prelude System.Random> fromInteger 12 :: Int
12
Prelude System.Random> fromInteger 12 :: Float
12.0
更改为Int,Float甚至Bool
Random
我们不知道Random实际上是什么,但我们可以将它“转发”为实例的类型,并且它始终100%工作。我不明白为什么会这样。
答案 0 :(得分:9)
我认为您错误地将类型类视为OO类并将类型继承与它们相关联而感到困惑。类型类非常不同,Haskell中没有类型继承,顺便说一句,它根本就不是一个弱点。你的例子实际上展示了很多Haskell的力量。
让我们分析一下random
的定义:
random :: RandomGen g => g -> (a, g)
它有一个签名g -> (a, g)
,表示它需要一些值g
并返回一些值a
和一些与输入g
相同类型的值,此签名中未指定Int
或Char
等特定类型,a
和g
为polymorphic,这意味着它们可以是任何类型。接下来是约束部分RandomGen g =>
,它表示实际上g
只能是一个具有类型类RandomGen
实例的类型,就在我链接的类的接口下面你会找到模块中定义的实例列表,它只包含RandomGen StdGen
,所以基本上我们可以看到g
为StdGen
。然后再看一下random
函数,找出它实际上被定义为类型类Random
的接口的一部分,它由类型变量a
参数化,我们已经已经在函数random
的签名中遇到,因此这意味着对函数Random a
的定义有random
约束。另请参阅其实例列表中的此类包含Random Int
,Random Double
,Random Bool
。
现在让我们回到你的例子。通过指定类型random (mkStdGen 12) :: (Bool, StdGen)
,您告诉编译器将random
视为random :: StdGen -> (Bool, StdGen)
,从中简单推导出RandomGen
和Random
的实例使用。这些实例实际上定义了函数的类型特定行为,这反过来保证任何可编译的代码都有意义。
如你所见,这一切与铸造完全无关。
答案 1 :(得分:2)
从我的角度来看,它实际上是一个Int
这是你错的地方。 fromInteger
有两种不同的实现:Integer -> Int
和Integer -> Float
。 Int
绝对没有fromInteger 12 :: Float
。
答案 2 :(得分:1)
从@MikeHartl的注释中可以看出,Haskell类型类不是OOP意义上的类。它们也不是接口。在您的情况下,将它们视为所有方法为静态的C ++模板类可能很有用:
template <typename T>
class Random
{
public:
static std::pair<T, StdGen> random(StdGen a);
}
“cast”根本不是“强制转换”,而是模板参数的明确限定:
std::pair<Int, StdGen> a = Random<Int>::random(mkStdGen 12);
std::pair<Int, Bool> a = Random<Bool>::random(mkStdGen 12);
同样适用于Num
:
template <typename a>
class Num
{
public:
static a fromInteger(Integer b);
}
int a = Num<int>::fromInteger(Integer(2222));
complex a = Num<complex>::fromInteger(Integer(3333));