[英]How to put constraints on the associated data?
我想聲明關聯數據始終是某個類的實例。
class (Context (Associated a b)) => Class a where
data Associated a :: * -> *
instance Context (Associated a b) where
func1 = error "func1"
但是,不在范圍內的自由變量b
阻止了我。 其中一個解決方案是從Context
復制類函數,但它看起來很難看。
class Class a where
data Associated a :: * -> *
-- duplicate all functions from class Context
contextFunc1 :: Associated a b -> String
instance Class a => Context (Associated a b) where
func1 = contextFunc1
是否有一種慣用的方法來對關聯數據類型進行約束,這些數據類型具有頭部未提及的變量?
編輯:我想保持與GHC 7.0.3的兼容性
正如@SjoerdVisscher所指出的那樣 ,在class
或instance
的=>
左側使用forall
實際上並不行 ,至少現在還沒有,盡管我的具體示例在ghc-7.4中有效。
這種方式似乎有效:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE UndecidableInstances #-}
class Context c where
func1 :: c -> String
class (forall b. Context (Associated a b)) => Class a where
data Associated a :: * -> *
newtype ClassTest = ClassTest { runClassTest :: String }
instance (forall b. Context (Associated ClassTest b)) => Class ClassTest where
data Associated ClassTest b = ClassTestAssoc b (b -> ClassTest)
instance Context (Associated ClassTest b) where
func1 (ClassTestAssoc b strFunc) = runClassTest $ strFunc b
main = putStr . func1 $ ClassTestAssoc 37 (ClassTest . show)
實例中額外的forall b
約束似乎有點丑陋和多余,但顯然它是必要的。
$ runghc-7.4.1 tFamConstraint0.hs
37
我沒有GHC 7.0.3,但我認為這應該可以使用它。
您可以像這樣手動傳遞字典(使用Context
= Show
作為示例):
{-# LANGUAGE ScopedTypeVariables, TypeFamilies, ExistentialQuantification #-}
data ShowDict a = Show a => ShowDict
class Class a where
data Associated a :: * -> *
getShow :: ShowDict (Associated a b)
-- Convenience function
getShowFor :: Class a => Associated a b -> ShowDict (Associated a b)
getShowFor _ = getShow
showAssociated :: Class a => Associated a b -> String
showAssociated a =
case getShowFor a of
ShowDict -> -- Show (Associated a b) is made available by this pattern match
show a
instance Class Int where
data Associated Int b = Foo deriving Show
getShow = ShowDict
main = print $ showAssociated Foo
這有點類似於您建議的復制功能,但優點是:
showAssociateds :: forall a b. Class a => [Associated a b] -> String
showAssociateds as =
case getShow :: ShowDict (Associated a b) of
ShowDict ->
show as
主要的缺點是使用getShow
總是需要一個顯式類型簽名(像getShowFor
這樣的getShowFor
可以減輕這種情況)。
一種慣用的方法是創建Context1
類。 假設我們有
class Context a where
func :: a -> String
我們可以概括為:
class Context1 f where
func1 :: Context a => f a -> String
然后你為所有Associated
s提供一個實例:
instance (Context1 (Associated a), Context b) => Context (Associated a b) where
func = func1
現在很容易寫出你想要的課程
instance Context1 (Associated a) => Class a where
data Associated a :: * -> *
並且您可以確定給定的Context1 (Associated a)
上下文確保了所需的forall b. Context b => Context (Associated ab)
forall b. Context b => Context (Associated ab)
上下文。
Hackage上有很多這種模式的例子,比如Show1 , Foldable1和Traversable1 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.