14

我想说的是,关联的数据始终是某个类的实例。

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 的兼容性

4

3 回答 3

6

正如@SjoerdVisscher 所指出的,在 aforall的左侧使用or实际上不是ok,至少现在还没有,尽管我的具体示例在 ghc-7.4 中确实有效。=>classinstance


这样它似乎工作:

{-# 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

于 2012-06-29T09:56:42.387 回答
5

我没有可用的 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

这有点类似于您提出的功能复制,但优点是:

  • 避免重复(`Context`的方法签名)
  • 在上下文中使用 `Show Baz` 比仅具有显示 `Baz` 的函数更强大,因为它允许您调用需要 `Show Baz` 的(库)函数,或使用隐含的实例,如 `Show [Baz] `:
showAssociateds :: forall a b. Class a => [Associated a b] -> String
showAssociateds as = 
  case getShow :: ShowDict (Associated a b) of
    ShowDict ->
      show as

主要缺点是使用getShow总是需要明确的类型签名(类似的函数getShowFor可以缓解这种情况)。

于 2012-06-30T11:49:37.987 回答
0

一种惯用的方法是创建一个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 a b)上下文。

在 Hackage 上有很多这种模式的例子,比如Show1Foldable1Traversable1

于 2015-09-17T18:54:56.597 回答