[英]How to establish an ordering between types in Haskell
我需要在* -> *
類型之間建立排序,基於一種類型的每個成員可以由另一種類型表示。 這是同態。
問題是我可以定義!<=!
的傳遞性!<=!
關系,但類型檢查無法搞清楚。 這也很模糊, Identity !<=! Maybe
Identity !<=! Maybe
可以從Identity !<=! Maybe
派生出來Identity !<=! Maybe
Identity !<=! Maybe
還是Identity !<=! Identity !<=! Maybe
Identity !<=! Identity !<=! Maybe
Identity !<=! Identity !<=! Maybe
,......每個派生都有一個不同的(但等價的) repr
定義。
所以我正在尋找其他方法來創造一種反思性和傳遞性的關系。
{-# LANGUAGE ScopedTypeVariables, TypeOperators, MultiParamTypeClasses, FlexibleInstances, UndecidableInstances, AllowAmbiguousTypes, OverlappingInstances #-}
import Control.Monad.Identity
import Data.Maybe
class x !<=! y where
repr :: x a -> y a
instance x !<=! x where
repr = id
instance Identity !<=! Maybe where
repr = return . runIdentity
instance Maybe !<=! [] where
repr = maybeToList
instance (x !<=! y, y !<=! z) => x !<=! z where
repr = r2 . r1
where r1 :: x a -> y a
r1 = repr
r2 :: y a -> z a
r2 = repr
注意:我在GHC 7.8上試過這個。 您可能必須刪除AllowAmbiguousTypes
。
編輯:我想做一些像repr (Identity 3 :: Identity Int) :: [Int]
問題是我們無法讓GHC對實例執行常規圖搜索。 在這種特殊情況下,如果GHC可以執行最短路徑算法,那將是更好的,因為我們的函數隨着路徑中的每個中間表示而變慢。
但是,我們可以通過將傳出邊的數量限制為1來使搜索在每個圖節點處明確,並且GHC可以處理該搜索。 這意味着每種類型最多只有一個直接表示:
{-# LANGUAGE FlexibleInstances, TypeOperators, MultiParamTypeClasses, FunctionalDependencies, UndecidableInstances, OverlappingInstances #-}
import Control.Monad.Identity
import Data.Maybe
class DirectRepr x y | x -> y where
directRepr :: x a -> y a
我們可以用DirectRepr
構建一個圖形:
instance DirectRepr Identity Maybe where
directRepr (Identity a) = Just a
instance DirectRepr Maybe [] where
directRepr = maybeToList
然后使用包裝類<=
遍歷它:
class x <= y where
repr :: x a -> y a
instance x <= x where
repr = id
instance (DirectRepr x y, y <= z) => x <= z where
repr = repr . directRepr
main = print (repr (Identity ()) :: [()]) -- [()]
它也適用於循環圖,因為當我們達到<=
的反身性情況時搜索停止了(感謝OverlappingInstances
):
data A a
data B a
data C a
instance DirectRepr A B where directRepr = undefined
instance DirectRepr B C where directRepr = undefined
instance DirectRepr C A where directRepr = undefined
foo :: A Int
foo = repr (undefined :: B Int)
如果起始類型導致循環,並且我們在循環中沒有端點類型,則搜索卡住並且我們得到上下文溢出。 這不應該打擾我們,因為這會使上下文溢出錯誤等同於簡單的“無實例”錯誤。
bar :: Maybe Int -- context overflow
bar = repr (undefined :: A Int)
僅通過推理可能無法做到這一點。 我使用Template Haskell創建了另一個解決方案,生成可以從更簡單的實例派生的所有實例。 庫的用法如下所示:
$(makeMonadRepr ''Identity ''Maybe [e| return . runIdentity |])
$(makeMonadRepr ''Identity ''IO [e| return . runIdentity |])
$(makeMonadRepr ''Maybe [t| MaybeT IO |] [e| MaybeT . return |])
$(makeMonadRepr ''IO [t| MaybeT IO |] [e| MaybeT . liftM Just |])
$(makeMonadRepr ''Maybe TH.ListT [e| maybeToList |])
$(makeMonadRepr TH.ListT [t| Trans.ListT IO |] [e| Trans.ListT . return |])
$(makeMonadRepr ''IO [t| Trans.ListT IO |] [e| Trans.ListT . liftM (:[]) |])
$(makeMonadRepr [t| MaybeT IO |] [t| Trans.ListT IO |] [e| Trans.ListT . liftM maybeToList . runMaybeT |])
這將生成可以從反身性或傳遞性中導出的所有實例。 插入一個調用makeMonadRepr
的新節點后,將創建所有可makeMonadRepr
邊,因此用戶可以擴展這樣的結構。
這可能不是最優雅的解決方案,所以我對其他想法持開放態度。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.