[英]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.