繁体   English   中英

Haskell function 中的通用型与刚性型

[英]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 ~ m1a ~ a1 ,因此使用这些类型参数调用randomRIOma在这里不是严格的)。 请注意,此处根本不涉及IOm可以是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 ~ m1a ~ a1 ,但也推断a ~ Int ,因为我们将一对Int s 传递给randomRIO a ~ a1a ~ 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM