2014-01-26 191 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')