3

私はかなり多形のライブラリを書き込もうとしています。伝えるよりも見せやすい状況に遭遇しました。これは少し次のようになります。

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
import Data.Map (Map)
import qualified Data.Map as Map

class Format f where type Target f
class Format f => Formatter x f where
  target :: forall y. Formatable y => Target f -> x -> y
class Formatable y where name :: y -> String

instance Formatable Integer where name = show
instance Formatable Int where name = show

split :: forall x f. (Format f, Formatter x f) => x -> f -> String -> [Either String (Target f)]
split = undefined

display :: forall x f. (Format f, Formatter x f) => f -> String -> x -> String
display f str x = let
  chunks = split x f str
  built = foldr apply "" chunks
  apply (Left s) accum = accum ++ s
  apply (Right t) accum = accum ++ name (target t x)
  in foldr apply "" chunks

Format基本的に、sの数を定義する多形sがありますTargetFormattableさまざまなフォーマットオプションに応答する方法を知っているオブジェクトもいくつかあります(ここでは単純に縮小されていますname)。

これらFormattablesはさまざまな方法で構成されており、さまざまなターゲットに対応できます。sは、基本的にとFormatterの間のルーターです。ターゲット(特定の形式から)が与えられると、適切なオブジェクトで応答します。FormatFormattableFormattable

これはすべてかなり抽象的なものです。次に例を示します。

  • DateFormatYear、、、などのターゲットを指定しMonthますDay
  • MonthType「2月」などの名前を持つニューFormattableタイプですInt
  • シンプルなものもありますinstance Formattable Int where name = show
  • DateTimeの型同義語である可能性があります(Int, MonthType, Int)

(明らかに、ここでは正しい値をパイプでつなぐなど、多くの機械を切り取っていますが、あなたはその考えを理解しています。)

display関数はかなり単純です。フォーマッタ、フォーマットを指定する文字列、表示するオブジェクトを受け取り、すべてを文字列にレンダリングします。

まず、文字列をターゲットと文字列に分割します。たとえば、日付フォーマッタは文字列"%Y-%m-%d"をに分割する場合があります[Right Year, Left "-", Right Month, Left "-", Right Day]split関数はそれを行い、ここで編集されています。

このdisplay関数は、各ターゲットのsを追跡しFormattable、文字列を累積するだけです。

または、少なくとも、そうなるはずです。

ただし、タイプチェックに失敗し、次のエラーが発生します。

Reduced.hs:20:16:
    Could not deduce (Target f ~ Target f0)
    from the context (Format f, Formatter x f)
      bound by the type signature for
                 display :: (Format f, Formatter x f) => f -> String -> x -> String
      at Reduced.hs:(19,5)-(24,30)
    NB: `Target' is a type function, and may not be injective
    Expected type: [Either [Char] (Target f0)]
      Actual type: [Either String (Target f)]
    In the return type of a call of `split'
    In the expression: split x f str
    In an equation for `chunks': chunks = split x f str
Failed, modules loaded: none.

そして、私は私の人生のために理由を理解することはできません。私は何が間違っているのですか?

4

1 に答える 1

2

問題は、をTarget f決定しないことfです。これは、関数が

target :: (Formatter f x, Formatable y) => Target f -> x -> y

呼び出すことはできません。どのタイプの注釈を付けてもtarget、それが何でfあるかを特定することはできないため、コンパイラーはFormatter使用するインスタンスを特定できません。100%確信はありませんが、おそらく解決策は、マルチパラメータ型クラスを使用せず、一方を他方の関数にすることですxfまた、おそらくFormatクラスを完全に削除する必要があります(型族を使用するためにクラスは必要ないことをご存知ですか?)。おそらくこのようなもの:

class Formatter x where
    type Format x
    target :: Formatable y => Format x -> x -> y
于 2012-04-08T01:03:17.373 に答える