3

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

但是在 haskell 中,我们可以简单地将类型类“向下转换”为该类型类的任何实例。比如fromIntegerwhich 返回一个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% 有效。我不明白为什么它有效。

4

3 回答 3

9

我认为您已经因为错误地将类型类视为 OO 类并将类型继承与它们关联起来而感到困惑。类型类非常不同,Haskell 中没有类型继承,顺便说一句,这根本不是一个弱点。您的示例实际上展示了 Haskell 的强大功能。

我们来分析 的定义random

random :: RandomGen g => g -> (a, g)

它有一个签名g -> (a, g),表示它接受一些值g并返回一些值a和一些与输入相同类型的值,在这个签名g中没有类似Int或指定的特定类型,并且是多态的,这意味着它们可以是绝对的随便哪种。然后是约束部分,它说实际上只能是具有类型类实例的类型,就在我链接到的类的接口下,您会找到在模块中定义的实例列表和它只包含,所以基本上我们可以看到。然后再看一遍CharagRandomGen g =>gRandomGenRandomGen StdGengStdGenrandom函数来发现它实际上被定义为类型类接口的一部分Random,它由类型变量参数化a,我们已经在函数的签名中遇到过random,因此这意味着Random a对函数定义的约束random。另请参阅该类在其实例列表中包含Random Int, Random Double, Random Bool

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

如您所见,这完全与选角无关。

于 2013-08-12T11:08:57.887 回答
2

从我的角度来看,它实际上是一个 Int

这是你错的地方。fromInteger:Integer -> Int和有两种不同的实现Integer -> Float。绝对没有Int参与fromInteger 12 :: Float

于 2013-08-12T10:57:23.787 回答
1

正如您从@MikeHartl Haskell 类型的评论中看到的那样,类型类不是 OOP 意义上的类。它们也不是接口。在您的情况下,将它们视为具有所有静态方法的 C++ 模板类可能会很有用:

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

“铸造”根本不是“铸造”,而是模板参数的明确限定:

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));
于 2013-08-12T11:05:11.777 回答