[英]Generic type vs rigid type in Haskell function
为什么 ghc 不抱怨以下 function 中的类型是刚性的?
play :: (Monad m, MonadIO m, Random a) => a -> a -> m a
play r1 r2 = do
randomRIO (r1, r2)
在上述情况下, m
实际上是IO
。 此代码编译。 而其他时候,当我在通用 function 中使用具体类型时,ghc 抱怨我使用的是具体类型。 我在这里想念什么吗?
例如,如果我说:
play :: (Monad m, MonadIO m, Random a) => a -> a -> m a
play r1 r2 = do
randomRIO (1, 6::Int)
这次 ghc 抱怨我使用的是刚性类型。 困扰我的是IO
在某种程度上是具体或刚性类型,因为它是IO
而不是Maybe
。
编辑 1:编译无错误的完整代码:
{-# LANGUAGE ConstraintKinds #-}
module Main where
-- showing how to program in the mtl style
import Control.Monad.IO.Class
import System.Random
type Game m a = (Monad m, MonadIO m, Random a, Show a)
-- step 1
play :: (Game m a) => a -> a -> m a
play r1 r2 = do
yell "hi"
randomRIO (r1, r2)
--rollDice r1 r2
-- step 2
yell :: (MonadIO m) => String -> m ()
yell str = liftIO $ putStrLn str
-- step 3
rollDice :: (Game m a) => a -> a -> m a
rollDice a1 a2 = liftIO $ randomRIO (a1,a2)
main :: IO ()
main = putStrLn ""
-- main :: IO ()
-- main = do
-- play 1 (6::Int) >>= print
-- play 'a' 'z' >>= print
-- putStrLn "done"
Code that doesn't compile:
{-# LANGUAGE ConstraintKinds #-}
module Main where
-- showing how to program in the mtl style
import Control.Monad.IO.Class
import System.Random
type Game m a = (Monad m, MonadIO m, Random a, Show a)
-- step 1
play :: (Game m a) => a -> a -> m a
play r1 r2 = do
yell "hi"
randomIO 'a' 'z'
--rollDice r1 r2
-- step 2
yell :: (MonadIO m) => String -> m ()
yell str = liftIO $ putStrLn str
-- step 3
rollDice :: (Game m a) => a -> a -> m a
rollDice a1 a2 = liftIO $ randomRIO (a1,a2)
main :: IO ()
main = putStrLn ""
-- main :: IO ()
-- main = do
-- play 1 (6::Int) >>= print
-- play 'a' 'z' >>= print
-- putStrLn "done"
这不编译:
{-# LANGUAGE ConstraintKinds #-}
module Main where
-- showing how to program in the mtl style
import Control.Monad.IO.Class
import System.Random
type Game m a = (Monad m, MonadIO m, Random a, Show a)
-- step 1
play :: (Game m a) => a -> a -> m a
play r1 r2 = do
yell "hi"
**randomRIO (1, 6::Int)**
--rollDice r1 r2
-- step 2
yell :: (MonadIO m) => String -> m ()
yell str = liftIO $ putStrLn str
-- step 3
rollDice :: (Game m a) => a -> a -> m a
rollDice a1 a2 = liftIO $ randomRIO (a1,a2)
main :: IO ()
main = putStrLn ""
-- main :: IO ()
-- main = do
-- play 1 (6::Int) >>= print
-- play 'a' 'z' >>= print
-- putStrLn "done"
更新
刚刚意识到我的困惑来自阅读错误的random
文档我在代码中使用random-1.2
时正在阅读random-1.1
文档。
供人参考:
random-1.1
randomRIO :: (a, a) -> IO a
random-1.2
randomRIO :: (Random a, MonadIO m) => (a, a) -> m a
我猜你正在使用random-1.2
,它使用更通用的类型randomRIO
。
在更新的 package 中, randomRIO
在单子m
和值类型a
上都是多态的。 它的类型是:
randomRIO :: (Random a, MonadIO m) => (a, a) -> m a
因此,当您编写时(为清楚起见重命名类型变量)
play :: (Monad m1, MonadIO m1, Random a1) => a1 -> a1 -> m1 a1
play r1 r2 = do
randomRIO (r1, r2)
GHC 推断ma ~ m1 a1
,这意味着m ~ m1
和a ~ a1
,因此使用这些类型参数调用randomRIO
( m
和a
在这里不是严格的)。 请注意,此处根本不涉及IO
: m
可以是IO
,但也可以是任何其他单子(在MonadIO
类中)。
相反,当你写
play :: (Monad m1, MonadIO m1, Random a1) => a1 -> a1 -> m1 a1
play r1 r2 = do
randomRIO (1, 6::Int)
GHC 再次推断ma ~ m1 a1
,因此m ~ m1
和a ~ a1
,但也推断a ~ Int
,因为我们将一对Int
s 传递给randomRIO
。 从a ~ a1
和a ~ Int
我们推断a1 ~ Int
会触发类型错误,因为a1
是刚性的。
如果我们只有一种更具体的randomRIO
类型(例如我们在random-1.2
之前的那种)
randomRIO :: (Random a) => (a, a) -> IO a
那么你的推理是正确的:我们会在类型推断期间得到m1 ~ IO
,这会导致错误,因为m1
是刚性的。 我们没有得到这个,因为randomRIO
更通用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.