haskell - Haskell函数中的通用类型与刚性类型
问题描述
为什么 ghc 不抱怨以下函数中的类型是刚性的?
play :: (Monad m, MonadIO m, Random a) => a -> a -> m a
play r1 r2 = do
randomRIO (r1, r2)
在上述情况下,m
实际上是IO
。此代码编译。而其他时候,当我在泛型函数中使用具体类型时,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.1
random-1.2
供人参考:
random-1.1
randomRIO :: (a, a) -> IO a
random-1.2
randomRIO :: (Random a, MonadIO m) => (a, a) -> m a
解决方案
我猜你正在使用random-1.2
,它使用更通用的类型randomRIO
.
在那个更新的包中,在 monad和 value typerandomRIO
上都是多态的。它的类型是: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 推断m a ~ m1 a1
,这意味着m ~ m1
和a ~ a1
,因此randomRIO
使用这些类型参数调用(m
并且a
在这里不是严格的)。请注意,IO
这里根本不涉及:m
可以是IO
,但也可以是任何其他 monad(在MonadIO
类中)。
相反,当你写
play :: (Monad m1, MonadIO m1, Random a1) => a1 -> a1 -> m1 a1
play r1 r2 = do
randomRIO (1, 6::Int)
GHC 再次推断m a ~ m1 a1
,因此m ~ m1
and 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
它更普遍。
推荐阅读
- machine-learning - 优化双射变换值是否会影响性能或准确性?
- c - 读取行缓冲流可以产生多行吗?
- python - 尝试制作加密程序 - 未知错误
- python - 计算 numpy 数组的条件中位数
- google-drive-api - Google Drive 视频嵌入随机获得 404
- java - 将值从一个类添加到另一个类时,Listview 不显示更新的值
- oauth-2.0 - AzureAD 的 OAuthPrompt/SigniIn 始终默认为 SSO 凭据。如何申请备用信用?
- database - Dapper ORM 不接受多个模型
- python - 在 pd.DataFrame 中查找最大值的最后一次出现的索引
- python - 将字符随机插入字符串数组最少次数