2016-07-22 34 views
1

如果我有一個TypeRep類型或Dynamic包含該類型的實例,有沒有什麼辦法可以找到一個已知類型類的適當實例,使我能夠在該類型類上調用一個函數,而無需知道完整輸入有問題?如果沒有這樣的特徵,是否有一個原因是不可能的,還是它沒有被實現?Haskell Dynamic/TypeRep:在不知道完整類型的情況下提取類型類的實例?

另外,有沒有一種方法(可能使用模板haskell)來運行一個類型類實例的所有類型的生成列表,以便我可以對每個類執行動態強制轉換並檢查結果?

我試圖做的是落實Eq和版本的Show實際顯示的數據Dynamic(儘可能)編寫更簡單,更普遍有用的單元測試的目的;我並不需要高性能,所以使用生成的代碼貫穿所有可能性是可以接受的。

+0

我很好奇你的用例在這裏。你爲什麼首先使用'Dynamic'? –

+0

@BenjaminHodgson我正在開發面嚮對象語言的解釋器,並希望允許將任意Haskell值嵌入到其自己的值類型中,以允許以Haskell代碼實現的對象方法將所需的任何值存儲在目標語言。 – Jules

回答

3

無法在運行時檢查某個類型是否是某個實例的實例。其中一個原因是導入一個模塊會引入新的實例,這會改變函數的結果(這對Haskell來說是非常糟糕的)。

你可以通過已知類型的列表。這個想法是你將該類型與該類型的實例一起存儲在GADT中,並匹配它以獲取所需的實例。我不太確定你想要什麼,但我認爲這是這樣的:

data EqShow where 
    JustEq :: (Typeable a, Eq a)   => Proxy a -> EqShow 
    EqShow :: (Typeable a, Eq a, Show a) => Proxy a -> EqShow 

有對類型的版本只有Eq和一個同時具有EqShow。我們的想法是,如果類型匹配,並使用Eq實例來檢查它們是否相等,我們可以匹配這些類型。如果有一個Show實例可用,我們也可以顯示結果。爲了存儲多個EqShows,我使用了一個哈希映射,以便我們可以查找這些類型。下面是完整的代碼:

{-# LANGUAGE GADTs #-} 
import   Data.Dynamic 
import   Data.Typeable 
import   Data.HashMap.Lazy (HashMap) 
import qualified Data.HashMap.Lazy as HM 

data EqShow where 
    JustEq :: (Typeable a, Eq a)   => Proxy a -> EqShow 
    EqShow :: (Typeable a, Eq a, Show a) => Proxy a -> EqShow 

justEq :: (Typeable a, Eq a) => Proxy a -> (TypeRep, EqShow) 
justEq p = (typeRep p, JustEq p) 

eqShow :: (Typeable a, Eq a, Show a) => Proxy a -> (TypeRep, EqShow) 
eqShow p = (typeRep p, EqShow p) 

-- | Different outcomes of testing equality. 
data Result 
    = DifferentType TypeRep TypeRep 
    | NotEq TypeRep (Maybe (String, String)) 
    | IsEq TypeRep (Maybe String) 
    | UnknownType TypeRep 
    deriving Show 

-- | Check if two Dynamics are equal. Show them if possible 
dynEq :: HashMap TypeRep EqShow -> Dynamic -> Dynamic -> Result 
dynEq hm da db 
    | ta /= tb = DifferentType ta tb 
    | otherwise = 
     case HM.lookup ta hm of 
     Just (EqShow p) -> checkEqShow p (fromDynamic da) (fromDynamic db) 
     Just (JustEq p) -> checkJustEq p (fromDynamic da) (fromDynamic db) 
     Nothing   -> UnknownType ta 
    where 
    ta = dynTypeRep da 
    tb = dynTypeRep db 

    -- Check if two results are equal and display them. 
    checkEqShow :: (Typeable a, Eq a, Show a) => Proxy a -> Maybe a -> Maybe a -> Result 
    checkEqShow _ (Just a) (Just b) 
     | a == b = IsEq ta (Just (show a)) 
     | otherwise = NotEq ta (Just (show a, show b)) 
    checkEqShow _ _ _ = UnknownType ta 

    -- Check if two results are equal without displaying them. 
    checkJustEq :: (Typeable a, Eq a) => Proxy a -> Maybe a -> Maybe a -> Result 
    checkJustEq p (Just a) (Just b) 
     | a == b = IsEq ta Nothing 
     | otherwise = NotEq ta Nothing 
    checkJustEq p _ _ = UnknownType ta 

您可以將它們製作已知類型的列表:

knownEqShows :: HashMap TypeRep EqShow 
knownEqShows = HM.fromList 
    [ eqShow (Proxy :: Proxy Int) 
    , eqShow (Proxy :: Proxy Char) 
    ] 

,並檢查他們:

> let a = toDyn 'a' 
> let b = toDyn 'b' 
> let c = toDyn (1 :: Int) 
> dynEq knownEqShows a a 
IsEq Char (Just "'a'") 
> dynEq knownEqShows a b 
NotEq Char (Just ("'a'","'b'")) 
> dynEq knownEqShows a c 
DifferentType Char Int 

生成稱爲EqShow S使用模板哈斯克爾將是困難。您可以爲不帶變量的類型創建版本(Double,Char等)但是如果您有一個變量(例如Maybe a),您將無法將其存儲在EqShow中,那麼您必須編寫所有版本(Maybe IntMaybe (Maybe Double)等),但有無數的這些。


當然,這將是非常容易的,而不是使用Dynamic使用另一種包裝(有可能):

data EqShowDynamic where 
    JustEqD :: (Typeable a, Eq a)   => a -> EqShowDynamic 
    EqShowD :: (Typeable a, Eq a, Show a) => a -> EqShowDynamic 

所以EqShow實例已經存在。

+0

查看案例我將需要使用這個,我認爲你使用包裝的更簡單的方法可能是最好的方式,但感謝所有的想法。 :) – Jules

+0

我其實決定走到一條相當不同的路線。現在結果是[hackage](http://hackage.haskell.org/package/constrained-dynamic)。 – Jules

相關問題