Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 哈斯克尔如何';下行';类型接口?_Haskell_Types_Downcast - Fatal编程技术网

Haskell 哈斯克尔如何';下行';类型接口?

Haskell 哈斯克尔如何';下行';类型接口?,haskell,types,downcast,Haskell,Types,Downcast,在oop中,例如java,当类型实际上是子类时,我们只能将super类向下转换为子类 但在haskell中,我们可以简单地将类型类“向下转换”到该类型类的任何实例中。例如fromInteger,它返回一个Num。从我的观点来看,它实际上是一个Int,因此它不能“向下转换”为Float,但它可以 Prelude System.Random> :t fromInteger a fromInteger a :: Num a => a Prelude System.Random> fr

在oop中,例如java,当类型实际上是子类时,我们只能将super类向下转换为子类

但在haskell中,我们可以简单地将类型类“向下转换”到该类型类的任何实例中。例如
fromInteger
,它返回一个
Num
。从我的观点来看,它实际上是一个Int,因此它不能“向下转换”为Float,但它可以

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
另一个例子是将
Random
更改为Int、Float甚至Bool

Prelude System.Random> let (a, g) = random (mkStdGen 12) :: (Int, StdGen)
Prelude System.Random> let (a, g) = random (mkStdGen 12) :: (Double, StdGen)
Prelude System.Random> let (a, g) = random (mkStdGen 12) :: (Bool, StdGen)
我们不知道Random实际上是什么,但我们可以将它“向下”转换为实例类型,并且它始终100%工作。我不明白它为什么有效

在我看来,它实际上是一个Int


这就是你错的地方。
fromInteger
有两种不同的实现:
Integer->Int
Integer->Float
。从@MikeHartl Haskell类型类的注释中可以看出,
fromInteger 12::Float
中绝对不涉及
Int
。它们也不是接口。在您的情况下,将它们视为C++模板类的方法是有用的,所有方法都是静态的:

template <typename T>
class Random
{
public:
   static std::pair<T, StdGen> random(StdGen a);
}

我认为您已经被错误地将类型类视为OO类并将类型继承与之关联而弄糊涂了。类型类是非常不同的,Haskell中没有类型继承,顺便说一句,这根本不是一个弱点。你的例子实际上证明了Haskell的很多能力

让我们分析一下以下定义:

它有一个签名
g->(a,g)
,表示它接受一些值
g
,并返回一些值
a
,以及一些与输入值
g
类型相同的值,在这个签名中没有指定像
Int
Char
这样的特定类型,
a
g
,这意味着它们可以是任何类型的。然后是约束部分
RandomGen g=>
,它说实际上
g
只能是一种类型,它有一个typeclass的实例,就在我链接到的类的接口下面。你会发现一个在模块中定义的实例列表,它只包含
RandomGen StdGen
,所以基本上我们可以把
g
看作
StdGen
。然后再次查看
random
函数,发现它实际上是定义为typeclass接口的一部分,该接口由类型变量
a
参数化,我们已经在函数
random
的签名中遇到了该变量,因此,这意味着对函数定义的约束是
Random
。还可以在实例列表中看到这个类包含
随机Int
随机Double
随机Bool

现在让我们回到您的示例。通过指定一个类型
random(mkStdGen 12):(Bool,StdGen)
您告诉编译器将
random
视为
random::StdGen->(Bool,StdGen)
,它只是从中推断出要使用的
RandomGen
random
的实例。这些实例实际上定义了函数的特定于类型的行为,这反过来又保证了任何可编译代码都是有意义的


正如您所见,这一切与铸造完全无关。

这与您所比较的铸造无关。首先,一切都是在编译时静态发生的。无法辨别对象是否“实际上是子类”。其次,这里根本没有子类-超类关系。它更像是一个接口
INum
,由
Int
Float
等实现,并包含一个方法
fromInteger
。转换在
fromInteger
中以任意方式定义,与类没有连接。因此,本主题可能会为您提供一些有用的见解和参考:@rkhayrov我将阅读:)感谢您的解释:)。哦,我完全错了。没有铸造,只有多态性。它们被称为多态函数(?),它有不同实例的不同实现,以及不同实例的不同返回类型。and::用于指定我们需要的实例。是的,包含任何多态参数的函数也被称为多态函数。是的,类型类实际上是在这些实例中获得其特定参数的(随机a中的
a
)实现。不,类型类函数的返回类型不一定需要依赖于实例-一个简单的例子是类型类
Show a
,它声明一个函数
Show::a->String
。不,
::
仅用于告诉编译器前面的表达式具有哪种类型,但在大多数情况下编译器可以推断,只是你的案例是个例外。谢谢你的回答。但我不知道cpp模板。它类似于java泛型函数?但据我所知。java泛型函数用于编译器的类型检查。它在运行时会向下转换。不幸的是,我不懂java:(该方法是静态的(没有实例引用),因此不需要向下转换。
std::pair<Int, StdGen> a = Random<Int>::random(mkStdGen 12);
std::pair<Int, Bool> a = Random<Bool>::random(mkStdGen 12);
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));
random :: RandomGen g => g -> (a, g)