14

関連するデータは常に特定のクラスのインスタンスであると述べたいと思います。

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

instance Context (Associated a b) where
  func1 = error "func1"

ただし、スコープ内にない自由変数bはこれを防ぎます。解決策の1つは、からクラス関数をコピーすることです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

頭に記載されていない変数を持つ関連データ型に制約を課す慣用的な方法はありますか?

編集:GHC7.0.3との互換性を維持したい

4

3 に答える 3

6

@SjoerdVisscherによって指摘されたように、 in a orforallの左側で使用することは実際に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` をコンテキストに持つことは、`Show Baz` を必要とする (ライブラリ) 関数を呼び出したり、`Show [Baz] のような暗黙のインスタンスを使用したりできるため、単に `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 に答える