如何对相关数据设置约束?

Bor*_*ris 14 haskell type-constraints type-families

我想声明关联数据始终是某个类的实例.

class (Context (Associated a b)) => Class a where
  data Associated a :: * -> *

instance Context (Associated a b) where
  func1 = error "func1"
Run Code Online (Sandbox Code Playgroud)

但是,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
Run Code Online (Sandbox Code Playgroud)

是否存在一种惯用的方法来对关联数据类型进行约束,这些数据类型具有头部未提及的变量?

编辑:我想保持与GHC 7.0.3的兼容性

lef*_*out 6

正如@SjoerdVisscher指出的那样,在a forall的左侧使用或者实际上并不正常,至少还没有,尽管我的具体示例在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)
Run Code Online (Sandbox Code Playgroud)

forall b实例中的额外约束似乎有点难看和多余,但显然它是必要的.

$ runghc-7.4.1 tFamConstraint0.hs
37

  • @leftaroundabout SPJ说这应该不起作用:http://hackage.haskell.org/trac/ghc/ticket/7019#comment:3 (4认同)

Fun*_*lad 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
Run Code Online (Sandbox Code Playgroud)

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

  • 避免重复("Context"的方法签名)
  • 在上下文中显示"显示Baz"比仅具有显示"Baz"的函数更强大,因为它允许您调用(库)需要"显示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
Run Code Online (Sandbox Code Playgroud)

主要缺点是使用getShow始终需要显式类型签名(类似函数getShowFor可以缓解这种情况).