haskell 'downcast'如何进行类型界面?

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

但是在haskell中,我们可以简单地将'downcast'类型类放入该类型类的任何实例中 . 如 fromInteger ,返回 Num . 从我的角度来看,它实际上是一个 Int 所以它不能'downcasted'到 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%工作 . 我不明白为什么会这样 .

回答(3)

3 years ago

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

让我们分析一下random的定义:

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

它有一个签名 g -> (a, g) ,它表示它需要一些值 g 并返回一些值 a 和一些与输入 g 相同类型的值,没有像此签名中指定的 IntChar 这样的特定类型, agpolymorphic ,意味着它们绝对可以是任何类型 . 接下来是约束部分 RandomGen g => ,它表示实际上 g 只能是一个具有类型类RandomGen实例的类型,在类I的接口下面've linked to you' ll找到模块中定义的实例列表,它将仅包含 RandomGen StdGen ,所以基本上我们可以将 g 视为 StdGen . 然后再看一下 random 函数,找出它实际上被定义为类型类Random的接口的一部分,它由类型变量 a 参数化,我们已经在函数 random 的签名中遇到了,所以这个意味着对函数 random 的定义有一个 Random a 约束 . 另请参见其实例列表中的此类包含 Random IntRandom DoubleRandom Bool .

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

如你所见,这一切都与铸造完全无关 .

3 years ago

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

这是你错了 . fromInteger 有两种不同的实现: Integer -> IntInteger -> Float . fromInteger 12 :: Float 中绝对没有涉及 Int .

3 years ago

正如您从@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));