[英]How to put constraints on the associated data?
I would like to state that the associated data is always an instance of a certain class. 我想声明关联数据始终是某个类的实例。
class (Context (Associated a b)) => Class a where
data Associated a :: * -> *
instance Context (Associated a b) where
func1 = error "func1"
However, the free variable b
that is not in scope prevents me from this. 但是,不在范围内的自由变量
b
阻止了我。 One of the solutions is to copy class functions from Context
, but it looks ugly. 其中一个解决方案是从
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
Is there an idiomatic way to put constraints on associated data type which has variables not mentioned in head? 是否有一种惯用的方法来对关联数据类型进行约束,这些数据类型具有头部未提及的变量?
edit: I would like to keep compatibility with GHC 7.0.3 编辑:我想保持与GHC 7.0.3的兼容性
As was pointed out by @SjoerdVisscher , using forall
on the left side of =>
in a class
or instance
is actually not ok , at least not yet, though my specific example does work in ghc-7.4. 正如@SjoerdVisscher所指出的那样 ,在
class
或instance
的=>
左侧使用forall
实际上并不行 ,至少现在还没有,尽管我的具体示例在ghc-7.4中有效。
This way it seems to work: 这种方式似乎有效:
{-# 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)
The extra forall b
constraint in the instance seems a bit ugly and redundant, but apparently it's necessary. 实例中额外的
forall b
约束似乎有点丑陋和多余,但显然它是必要的。
$ runghc-7.4.1 tFamConstraint0.hs
$ runghc-7.4.1 tFamConstraint0.hs
3737
I don't have GHC 7.0.3 available, but I think this should work with it. 我没有GHC 7.0.3,但我认为这应该可以使用它。
You could pass the dictionaries around manually like this (using Context
= Show
as an example): 您可以像这样手动传递字典(使用
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
This is somewhat similar to the function copying you propose, but advantages are: 这有点类似于您建议的复制功能,但优点是:
showAssociateds :: forall a b. Class a => [Associated a b] -> String
showAssociateds as =
case getShow :: ShowDict (Associated a b) of
ShowDict ->
show as
The main disadvantage is that using getShow
always requires an explicit type signature (functions like getShowFor
can mitigate this). 主要的缺点是使用
getShow
总是需要一个显式类型签名(像getShowFor
这样的getShowFor
可以减轻这种情况)。
One idiomatic way is to create a Context1
class. 一种惯用的方法是创建
Context1
类。 Supposing we have 假设我们有
class Context a where
func :: a -> String
we might generalize as: 我们可以概括为:
class Context1 f where
func1 :: Context a => f a -> String
Then you give a single instance for all Associated
s: 然后你为所有
Associated
s提供一个实例:
instance (Context1 (Associated a), Context b) => Context (Associated a b) where
func = func1
Now it is easy to write the class you want as 现在很容易写出你想要的课程
instance Context1 (Associated a) => Class a where
data Associated a :: * -> *
and you can be sure that the given Context1 (Associated a)
context ensures the desired forall b. Context b => Context (Associated ab)
并且您可以确定给定的
Context1 (Associated a)
上下文确保了所需的forall b. Context b => Context (Associated ab)
forall b. Context b => Context (Associated ab)
context. forall b. Context b => Context (Associated ab)
上下文。
There are many examples of this pattern on Hackage, like Show1 , Foldable1 , and Traversable1 . Hackage上有很多这种模式的例子,比如Show1 , Foldable1和Traversable1 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.