問題是我們不能將Synonym
放在實例頭部,因爲它是一個類型族,並且我們不能寫出像(forall x. Show (Synonym x)) => ...
這樣的「通用量化」約束,因爲Haskell中沒有這樣的東西。
然而,我們可以用兩件事情:
forall x. f x -> a
相當於(exists x. f x) -> a
singletons
的去官能讓我們把類型家庭陷入實例頭反正。
所以,我們定義了一個生存的包裝上singletons
風格類型功能的工作原理:
data Some :: (TyFun k * -> *) -> * where
Some :: Sing x -> f @@ x -> Some f
而且我們還包括LiftedType
的去官能化符號:
import Data.Singletons.TH
import Text.Read
import Control.Applicative
$(singletons [d| data LiftedType = V1 | V2 | V3 deriving (Eq, Show) |])
type family Synonym t where
Synonym V1 = Int
Synonym V2 =()
Synonym V3 = Char
data SynonymS :: TyFun LiftedType * -> * -- the symbol for Synonym
type instance Apply SynonymS t = Synonym t
現在,我們可以使用Some SynonymS -> a
而不是forall x. Synonym x -> a
,這種形式也可以在實例中使用。
instance Show (Some SynonymS) where
show (Some SV1 x) = show x
show (Some SV2 x) = show x
show (Some SV3 x) = show x
instance Read (Some SynonymS) where
readPrec = undefined -- I don't bother with this now...
parseLTandSynonym :: LiftedType -> String -> String
parseLTandSynonym lt x =
case (toSing lt) of
SomeSing (slt :: SLiftedType lt') -> parseSynonym slt x
parseSynonym :: forall lt. SLiftedType lt -> String -> String
parseSynonym slt flv =
case (readEither flv :: Either String (Some SynonymS)) of
Left err -> "Can't parse synonym: " ++ err
Right x -> "Synonym value: " ++ show x
這並不直接爲我們提供Read (Synonym t)
爲t
任何具體的選擇,但我們仍然可以讀上存在標籤Some SynonymS
然後模式匹配檢查,如果我們有正確的類型(如果它不是失敗對)。這幾乎涵蓋了所有的用例read
。
如果這還不夠好,我們可以使用另一個包裝器,並將Some f
實例提升爲「通用量化」實例。
data At :: (TyFun k * -> *) -> k -> * where
At :: Sing x -> f @@ x -> At f x
At f x
相當於f @@ x
,但我們可以編寫實例吧。特別是,我們可以在這裏寫一個明智的通用Read
實例。
instance (Read (Some f), SDecide (KindOf x), SingKind (KindOf x), SingI x) =>
Read (At f x) where
readPrec = do
Some tag x <- readPrec :: ReadPrec (Some f)
case tag %~ (sing :: Sing x) of
Proved Refl -> pure (At tag x)
Disproved _ -> empty
我們首先解析Some f
,然後檢查解析式指數是否等於我們想解析指標。這是我上面提到的用於解析具有特定索引的類型的抽象。這樣更方便,因爲我們在At
的模式匹配中只有一個案例,無論我們有多少指數。通知雖然SDecide
約束。它提供了%~
方法,如果我們在單例數據定義中包含deriving Eq
,singletons
派生它。把這個使用方法:
parseSynonym :: forall lt. SLiftedType lt -> String -> String
parseSynonym slt flv = withSingI slt $
case (readEither flv :: Either String (At SynonymS lt)) of
Left err -> "Can't parse synonym: " ++ err
Right (At tag x) -> "Synonym value: " ++ show (Some tag x :: Some SynonymS)
我們也可以At
和Some
更容易一點之間的轉換:
curry' :: (forall x. At f x -> a) -> Some f -> a
curry' f (Some tag x) = f (At tag x)
uncurry' :: (Some f -> a) -> At f x -> a
uncurry' f (At tag x) = f (Some tag x)
parseSynonym :: forall lt. SLiftedType lt -> String -> String
parseSynonym slt flv = withSingI slt $
case (readEither flv :: Either String (At SynonymS lt)) of
Left err -> "Can't parse synonym: " ++ err
Right atx -> "Synonym value: " ++ uncurry' show atx
我也想要這個,但據我所知,這是不可能的。你只需要1班,但我認爲使用單身。 – bennofs
爲什麼不只是'parseF :: forall lt。 (Read(Synonym lt),Show(Synonym lt))=> SLiftedType lt - > String - > String'?據我瞭解,它足以滿足您的需求。 –
@AndrásKovács我在我的激勵範例中增加了一些更多的背景。 「SLiftedType lt''的值是不知道的 - 我試圖將''(String,String)''解析爲''(LiftedType,String)'',然後到''(SLiftedType lt,同義詞lt)'',但隱藏了''SomeSing''語句中的依賴類型部分。 – dbeacham