【问题标题】:Generic type vs rigid type in Haskell functionHaskell函数中的通用类型与刚性类型
【发布时间】:2021-03-14 02:57:57
【问题描述】:

为什么 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.2 时,我正在阅读random-1.1 文档。

供大家参考:

random-1.1
randomRIO :: (a, a) -> IO a

random-1.2
randomRIO :: (Random a, MonadIO m) => (a, a) -> m a  

【问题讨论】:

  • 你使用m a,这意味着m 是一个接受一个类型,然后产生一个具体类型的东西。例如,您可以使用IOMaybe[] 等。虽然这里Maybe[] 当然不会满足MonadIO 类型约束。
  • @WillemVanOnsem 如果我说 randomRIO (1,2::Int),ghc 会抱怨。所以我可以将m 设为具体类型,但不能将a 设为?
  • 如果你不确定它有什么问题,为什么不分享编译的代码?就目前而言,这里没有什么要回答的。
  • @daydaynatation:不,IO 不是具体类型。您可以创建IO IntIO () 等,但不能创建IO。因此,IO 基本上是一个接受类型(Int() 等)并产生类型的函数。 m 因此有种m :: * -> *
  • "m 实际上是 IO"。上面的代码不需要mIO。它允许m 成为IO(或其他任何MonadIO)。因此,如果您以强制mIO 的方式调用此代码,实际上没有问题。如果您有一些无法编译的代码并且您不明白为什么,那么如何显示该代码?

标签: haskell


【解决方案1】:

我猜你正在使用random-1.2,它使用更通用的randomRIO 类型。

在更新后的包中,randomRIO 在 monad 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 ~ m1a ~ a1,因此使用这些类型参数调用randomRIOma 在此处不是严格的)。请注意,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 ~ m1a ~ a1,但也推断a ~ Int,因为我们将一对Ints 传递给randomRIO。从a ~ a1a ~ Int 我们推断a1 ~ Int 会触发类型错误,因为a1 是刚性的。

如果我们只有一个更具体的 randomRIO 类型(例如我们在 random-1.2 之前的类型)

randomRIO :: (Random a) => (a, a) -> IO a

那么你的推理是正确的:我们会在类型推断期间得到m1 ~ IO,这会导致错误,因为m1 是刚性的。我们没有得到这个,因为randomRIO 更笼统。

【讨论】:

  • 抱歉,我查过了。 randomRIO :: (a, a) -> IO a 。所以randomRIO是一个具体的类型。看这里:hackage.haskell.org/package/random-1.1/docs/System-Random.html
  • 请问你在哪里找到的:randomRIO :: (Random a, MonadIO m) => (a, a) -> m a ?
  • 哇,刚发现random-1.2比randome-1.1有更新
  • 1.2 几乎是对随机数的完全重写。
  • @daydaynatation 添加了指向具有此类类型的 random-1.2 的链接。我忘了它曾经有不太通用的 IO 类型。
猜你喜欢
  • 2011-03-12
  • 1970-01-01
  • 2013-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-10
  • 2021-12-31
  • 1970-01-01
相关资源
最近更新 更多