cod*_*e4j 3 haskell types downcast
在oop中,例如java,当类型实际上是子类时,我们只能将超类转发为子类.
但是在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
Run Code Online (Sandbox Code Playgroud)
另一个例子是改为RandomInt,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)
Run Code Online (Sandbox Code Playgroud)
我们不知道Random实际上是什么,但我们可以将它"转发"为实例的类型,并且它始终100%工作.我不明白为什么会这样.
我认为您错误地将类型类视为OO类并将类型继承与它们相关联,这让您感到困惑.类型类非常不同,Haskell中没有类型继承,顺便说一句,它根本就不是一个弱点.您的示例实际上展示了很多Haskell的强大功能.
让我们来分析一下这个定义random:
random :: RandomGen g => g -> (a, g)
Run Code Online (Sandbox Code Playgroud)
它有一个签名g -> (a, g),它表示它需要一些值g并返回一些值a和一些与输入相同类型的值,这个签名g中没有类似Int或Char指定的特定类型,a并且g是多态的,这意味着它们可以是绝对的随便哪种.接下来是约束部分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要使用的类型.这些实例实际上定义了函数的特定于类型的行为,这反过来又保证了任何可编译的代码都有意义.
如你所见,这一切都与铸造完全无关.