首页 文章

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

提问于
浏览 591
3

在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 回答

  • 9

    我认为你错误地将类型类视为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 要使用的实例 . 这些实例实际上定义了函数的特定于类型的行为,这反过来又保证了任何可编译的代码都有意义 .

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

  • 2

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

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

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

相关问题