簡體   English   中英

如何對相關數據設置約束?

[英]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指出的那樣 ,在classinstance=>左側使用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

這有點類似於您建議的復制功能,但優點是:

  • 避免重復(“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

主要的缺點是使用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上有很多這種模式的例子,比如Show1Foldable1Traversable1

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM