hip*_*afe 2 enums haskell typeclass deriving
我今天写了这个:
data Door = A | B | C
deriving (Eq,Bounded,Enum)
instance Random Door where
randomR (lo,hi) g = (toEnum i, g')
where (i,g') = randomR (fromEnum lo, fromEnum hi) g
random = randomR (minBound,maxBound)
Run Code Online (Sandbox Code Playgroud)
我认为这对于任何枚举都是大致可复制的.我尝试将Random放入derinding子句中,但是失败了.
然后我搜索了网络,发现了这个:
请为随机#21提供(Enum a,Bounded a)的实例
一些引用似乎回答了我的问题,除了我不太了解它们:
你有什么实例,实例(Bounded a,Enum a)=>随机a在哪里......?不可能有这样的实例,因为它会与其他所有实例重叠.
这将阻止任何用户派生的实例....
为什么不能通过derinding子句或至少使用默认实现来实现自动化.
为什么这不起作用?
instance (Bounded a, Enum a) => Random a where
randomR (lo,hi) g = (toEnum i, g')
where (i,g') = randomR (fromEnum lo, fromEnum hi) g
random = randomR (minBound,maxBound)
Run Code Online (Sandbox Code Playgroud)
注释指的是在Haskell中(实际上在带有FlexibleInstances扩展的Haskell中),实例匹配是通过匹配类型而不考虑约束来完成的. 之后的类型匹配成功,约束进行校验,如果他们不满意会产生错误.所以,如果你定义:
instance (Bounded a, Enum a) => Random a where ...
Run Code Online (Sandbox Code Playgroud)
您实际上是为每种类型定义一个实例a,而不仅仅是a具有Bounded和Enum实例的类型.就像你写的那样:
instance Random a where ...
Run Code Online (Sandbox Code Playgroud)
这将可能与任何其他库定义或用户定义的实例冲突,例如:
newtype Gaussian = Gaussian Double
instance Random Gaussian where ...
Run Code Online (Sandbox Code Playgroud)
有办法解决这个问题,但整个过程变得非常混乱.此外,它可能会导致一些神秘的编译类型错误消息,如下所述.
具体来说,如果您将以下内容放入模块中:
module RandomEnum where
import System.Random
instance (Bounded a, Enum a) => Random a where
randomR (lo,hi) g = (toEnum i, g')
where (i,g') = randomR (fromEnum lo, fromEnum hi) g
random = randomR (minBound,maxBound)
Run Code Online (Sandbox Code Playgroud)
你会发现你需要FlexibleInstances扩展来允许对实例进行约束.这很好,但如果你添加它,你会发现你需要UndecidableInstances扩展名.这可能不太好,但是如果你添加它,你会发现你randomR在randomR定义的RHS 上调用时会出错.GHC已确定您现在定义的实例与内置实例重叠Int.(这实际上是巧合,Int两者Bounded兼而有Enum- 它也会与内置实例重叠Double,但两者都没有.)
无论如何,你可以通过使你的实例重叠来解决这个问题,以便以下内容:
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
module RandomEnum where
import System.Random
instance {-# OVERLAPPABLE #-} (Bounded a, Enum a) => Random a where
randomR (lo,hi) g = (toEnum i, g')
where (i,g') = randomR (fromEnum lo, fromEnum hi) g
random = randomR (minBound,maxBound)
Run Code Online (Sandbox Code Playgroud)
实际上会编译.
这很好,但你最终可能会收到一些奇怪的错误信息.通常,以下程序:
main = putStrLn =<< randomIO
Run Code Online (Sandbox Code Playgroud)
会产生明智的错误信息:
No instance for (Random String) arising from a use of `randomIO'
Run Code Online (Sandbox Code Playgroud)
但是在上面的例子中,它变成了:
No instance for (Bounded [Char]) arising from a use of ‘randomIO’
Run Code Online (Sandbox Code Playgroud)
因为您的实例匹配,String但GHC无法找到Bounded String约束.
无论如何,一般来说,Haskell社区已经避免将这些类型的catch-all实例放入标准库中.事实上,他们需要UndeciableInstances扩展和OVERLAPPABLEpragma并可能在程序中引入一堆不良实例,这些都会让人感觉不好.
因此,虽然在技术上可以添加这样的实例System.Random,但它永远不会发生.
同样,可以允许Random自动派生任何类型的Enum和Bounded,但社区不愿意添加额外的自动派生机制,特别是对于Random那些不经常使用的类型类(比较说Show或者Eq) .所以,再一次,它永远不会发生.
相反,允许方便的默认实例的标准方法是定义一些可以在显式实例定义中使用的辅助函数,这就是您链接的提案底部的建议.例如,可以在以下位置定义以下功能System.Random:
defaultEnumRandomR :: (Enum a, RandomGen g) => (a, a) -> g -> (a, g)
defaultEnumRandomR (lo,hi) g = (toEnum i, g')
where (i,g') = randomR (fromEnum lo, fromEnum hi) g
defaultBoundedRandom :: (Random a, Bounded a, RandomGen g) => g -> (a, g)
defaultBoundedRandom = randomR (minBound, maxBound)
Run Code Online (Sandbox Code Playgroud)
人们会写:
instance Random Door where
randomR = defaultEnumRandomR
random = defaultBoundedRandom
Run Code Online (Sandbox Code Playgroud)
这是唯一有机会成功的解决方案System.Random.
如果确实如此,并且您不喜欢必须定义显式实例,那么您可以自由地坚持:
instance {-# OVERLAPPABLE #-} (Bounded a, Enum a) => Random a where
randomR = defaultEnumRandomR
random = defaultBoundedRandom
Run Code Online (Sandbox Code Playgroud)
在你自己的代码中.
| 归档时间: |
|
| 查看次数: |
131 次 |
| 最近记录: |