2014-01-26 190 views
2

考虑:返回类型

import System.Random 

data Rarity = Common | Uncommon | Rare | Legendary 
       deriving (Show, Eq, Ord, Enum, Bounded) 

我想实例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') 

,但是这虽然失败,因为即使它知道它需要从所有比较随机Ord,它不能告诉我真正想要的是一个随机Float。因此,我认为我需要键入的random g的返回值,但我遇到了一个问题:

(random g :: (Float, ???))

因为它是一个元组,我该怎么申报为第二类型?我尝试了类似(random g :: (Float, RandomGen t)),但不能推断出t,我不知道如何将它与g的类型相匹配。我得到它通过使用StdGen到处工作,而不是RandomGen g,但后来我不能实例Random,它可能应该与任何随机RandomGen,无论如何应用。说实话,我甚至不在乎它是什么,因为我只是把它传递开来,但感觉就像我被迫。我试图做找出正确的类型如下:

randomRarity [email protected](RandomGen t) = case (random g :: (Float, RandomGen t)) of 
    ... 

但操作上类型构造(私人的,不会少),不类型,所以我认为这是一个根本错误的做法。

推理一下后,终于为我工作的事情是以下几点:

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 

这是相当简洁,但它宣称多数民众赞成远离其意在影响事情的另一个变量,这意味着当你看到r'时,你必须做一个双重处理,然后去弄清它是什么,然后回来。最糟糕的是,这让我的好奇心得不到满足。所以我的问题是:

在这种情况下,有一种方法可以告诉random g在我通过正确声明元组中的第二个类型来调用它时生成一个Float,或者以某种方式从g推断它。或者,如果失败了,是否有一种方法可以在不声明r'之类的其他变量的情况下约束r的类型?

回答

3

我认为在这种情况下,最简单的解决方案是明确键入您正在比较的数字之一。这迫使(<)操作中要专门到Float,反过来强迫rFloat

randomRarity :: (RandomGen g) => g -> (Rarity, g) 
randomRarity g = case random g of 
    (r,g') | r < (0.50 :: Float) -> (Common, g') 
     | r < 0.75   -> (Uncommon, g') 
     | r < 0.88   -> (Rare, g') 
     | otherwise   -> (Legendary, g') 

否则,这是在Haskell一个已知的问题,标准的解决方案是使用asTypeOf :: a -> a -> a。它只是一个限制类型的const的类型限制版本。所以在这种情况下,如果我们无法知道的random g :: (Float, ???)确切类型,我们可以从Control.Arrow导入first和地图上方。例如像这样一对的第一个元素:

randomRarity g = case first (`asTypeOf` (0 :: Float)) $ random g of 
    ... 

特别是,添加表情是类型

first (`asTypeOf` (0 :: Float)) :: (Float, a) -> (Float, a) 

这不是很简洁的,但它的工作原理,并定位于一个地方random g使用。

5

如果你使用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') 

或者,如果你想使用标准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')