use*_*057 2 random haskell types
鉴于:
import System.Random
data Rarity = Common | Uncommon | Rare | Legendary
deriving (Show, Eq, Ord, Enum, Bounded)
Run Code Online (Sandbox Code Playgroud)
我想要实例Random,所以我开始使用类似的东西:
randomRarity :: (RandomGen g) => g -> (Rarity, g)
randomRarity g = case random g of
(r,g') | r < 0.50 -> (Common, g')
| r < 0.75 -> (Uncommon, g')
| r < 0.88 -> (Rare, g')
| otherwise -> (Legendary, g')
Run Code Online (Sandbox Code Playgroud)
但这失败了,因为即使它知道它需要Ord从所有比较中随机,它也无法分辨出我真正想要的是随机的Float.所以我想我需要键入返回值random g,但是我遇到了一个问题:
(random g :: (Float, ???))
由于它是一个元组,我将什么声明为第二类?我尝试了类似的东西(random g :: (Float, RandomGen t)),但t无法推断,我不知道如何将其与g类型相匹配.我通过StdGen在任何地方使用它来工作而不是RandomGen g,但是我不能实例Random,并且它可能应该随机地工作RandomGen,无论如何.而且,说实话,我甚至不关心它是什么,因为我只是传递它,但感觉就像我被迫.我尝试通过执行以下操作找出正确的类型:
randomRarity g@(RandomGen t) = case (random g :: (Float, RandomGen t)) of
...
Run Code Online (Sandbox Code Playgroud)
但是这对类型构造函数(私有的,不少)进行操作,而不是类型,所以我认为这是一种根本错误的方法.
在推理之后,最终对我有用的事情如下:
randomRarity g = case random g of
(r,g') | r' < 0.50 -> (Common, g')
| r' < 0.75 -> (Uncommon, g')
| r' < 0.88 -> (Rare, g')
| otherwise -> (Legendary, g')
where r' = r :: Float
Run Code Online (Sandbox Code Playgroud)
这是相当简洁的,但它声明了另一个远离它想要影响的东西的变量,这意味着当你看到它r'然后去弄清楚它是什么时你必须做一个双重的,然后回来.最糟糕的是,它让我的好奇心不满意.所以我的问题是:
在这个上下文中是否有一种方法可以告诉random g我在通过正确声明元组中的第二个类型来调用它时生成一个Float,或者通过某种方式从中推断它g.或者,如果没有这个,有没有办法约束类型r而不声明另一个变量r'?
如果您使用的是GHC,则可以使用-XScopedTypeVariables(或添加{-# LANGUAGE ScopedTypeVariables #-}到文件顶部)代码
randomRarity g = case random g of
(r::Float,g') | r < 0.50 -> (Common, g')
| r < 0.75 -> (Uncommon, g')
| r < 0.88 -> (Rare, g')
| otherwise -> (Legendary, g')
Run Code Online (Sandbox Code Playgroud)
或者如果你想使用标准的Haskell,你可以使用
randomRarity g = let (r,g') = random g
in case r :: Float of
r | r < 0.50 -> (Common, g')
| r < 0.75 -> (Uncommon, g')
| r < 0.88 -> (Rare, g')
| otherwise -> (Legendary, g')
Run Code Online (Sandbox Code Playgroud)