我很難把這個問題放在一起。讓我們一步一步地嘗試:
我有一個 Haskell 型別類(代表鍵盤布局僅供參考):
class Data key => Palantype key where
-- the instance has to provide this mapping
keyCode :: key -> Char
-- but the reverse is required, too:
toKeys :: Char -> [key]
我可以為toKeys基于Typeable和提供一個(效率不高的)默認實作Data:
-- | override for efficiency
toKeys :: key -> Char -> [key]
toKeys k c =
let t = dataTypeOf k
ks = fromConstr . indexConstr t <$> [1..(maxConstrIndex t)]
m = foldl (\m k -> Map.insertWith ( ) (keyCode k) [k] m) Map.empty ks
in fromMaybe [] $ Map.lookup c m
...上面的代碼有效。好的。
但是,有一個問題。默認實作需要key作為第一個引數,原因是:Data需要型別的運行時表示。這是由 提供的DataType,使用dataTypeOf :: a -> DataType.
我必須調整型別簽名toKeys并始終提供一個不太有意義的虛擬密鑰。我明白這就是Data.Data作業原理。但是有沒有辦法根據型別變數獲得相同的魔法key?
還有就是功能typeRep中Data.Typeable,似乎作業方式:
typeRep :: forall proxy a. Typeable a => proxy a -> TypeRep
但是Data( fromConstr, indexConstr, maxConstrIndex) 的所有作業都依賴于運行時表示DataType(出于某種原因?)。
一個優雅的解決方案Data.Proxied:
toKeys :: Char -> [key]
toKeys c =
let t = dataTypeOfProxied (Proxy :: Proxy key)
ks = fromConstr . indexConstr t <$> [1..(maxConstrIndex t)]
m = foldl (\m k -> Map.insertWith ( ) (keyCode k) [k] m) Map.empty ks
in fromMaybe [] $ Map.lookup c m
uj5u.com熱心網友回復:
GHCi 中的快速測驗:
> dataTypeOf (undefined :: Int)
DataType {tycon = "Prelude.Int", datarep = IntRep}
這表明它dataTypeOf并不真正需要運行時值,第一個引數僅用于其型別。你可以(并且應該)寫一些類似的東西
toKeys :: forall key . Data key => Char -> [key]
toKeys c =
let t = dataTypeOf (undefined :: key)
...
在我看來,這個界面并不是今天應該有的樣子,但由于歷史原因,我們仍然擁有它。當Data被設計出來,我想,我們沒有AllowAmbiguousTypes, TypeApplications,所以我們用“未評估”引數和/或代理。
如果Data是今天設計的,我想我們會有歧義的型別
dataTypeOf :: forall a . Data a => DataType
我們會將其用作dataTypeOf @key.
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/326049.html
