11

GHC拡張機能を使用して独自のデータ型で関数をエミュレートすることは可能ですか? 私がやりたいことは、例えば

(架空の構文)

data MyFunc = MyFunc String (Int->Int)

instance (Int->Int) MyFunc where
    ($) (MyFunc _ f) i = f i

inc = MyFunc "increment" (1+)

test = inc 1

つまり、何らかのメタ情報を含み、パターン マッチングが可能なデータですが、通常の関数のように呼び出すこともできます。$$これで、 and callのような独自の中置演算子を定義できることがわかりましたinc $$ 1が、通常の関数呼び出し構文を使用できると、組み込み DSL で非常に役立ちます。

4

3 に答える 3

18

はい、限られた範囲で行うことができます。

しかし、まず必要になるのは

{-# LANGUAGE Rank2Types #-}

定義しましょう

data M a b = M { name :: Int -> String -> String, eval :: a -> b }

私はあなたの名前にもっと構造を追加しているので、より良いショーサポートを得ることができます. ;)

次に、クラスを定義しましょう。

class Magic m where
    magic :: M a b -> m a b

instance Magic M where
    magic = id

instance Magic (->) where
    magic (M _ f) = f

ここで、次のタイプを検討してください。

type MyFunc a b = forall m. Magic m => m a b

の結果の型はまたは のmagicいずれかです。(a -> b)M a b

そのため、のメンバーとして使用できますMyFunc。インスタンスをディスパッチできないため、このタイプはやや満足のいくものではありませんが、

inc :: MyFunc Int Int
inc = magic (M (const (showString "inc")) (+1))

test :: Int
test = inc 1

うまく動作します。

それらを表示するためのかなり良い方法を作ることさえできます。show on を使用することはできませんがMyFunc、 に対して定義することはできますM

instance Show (M a b) where
    showsPrec d (M s _) = s d

次に、 を取得するために適用できる関数M a b(および拡張によって any MyFunc) を作成できますM a b

m :: M a b -> M a b
m = id

MyFuncそして、 sを表示する特別なコンビネータを定義できます。

showM :: MyFunc a b -> String
showM f = show (m f)

それから私たちは遊ぶことができます。s の合成を定義できますMyFunc

infixr 9 .#
(.#) :: MyFunc b c -> MyFunc a b -> MyFunc a c
f .# g = magic (M 
    (\d -> showParen (d > 9) $ showsPrec 10 (m f) . 
                               showString " . " . 
                               showsPrec 9 (m g)) 
    (f . g))

inc2 :: MyFunc Int Int
inc2 = inc .# inc

test2 :: Int
test2 = inc2 1

bar, baz :: String
bar = showM inc
baz = showM inc2

また、名前に十分な構造を与えたので、より複雑な構成でも、不必要な括弧なしで正しい括弧を取得できます。

*Main> showM $ inc2 .# inc
"(inc . inc) . inc"

*Main> showM $ inc .# inc2
"inc . inc . inc"

ただし、 のインスタンスは定義できないことにMyFunc注意しtypenewtypeください。インスタンスを定義するには、それらを onMで定義してから、 を使用mしてその型に変換し、暗黙的なディスパッチが取得する型を持つようにする必要があります。

ランク 2 のタイプであるため、これらをローカル コンテキストで頻繁に使用する場合は、NoMonoLocalBindsおよび/またはをオンにすることもできますNoMonomorphismRestriction

于 2011-06-26T15:54:59.170 に答える
5

いいえ、構文f eはオーバーロードできません。にはftype が必要S -> Tです。

しかし、深い埋め込みを行う場合、つまり、計算の代わりに関数に構文ツリーを構築させる場合、EDSL で多くのことができます。

于 2011-06-26T09:35:20.900 に答える
3

関数呼び出し構文を直接オーバーロードすることはできません。

独自のカスタム矢印タイプを作成できます--- Control.Arrowを参照してください。次に、矢印表記を使用して「関数」を呼び出すことができます({-# LANGUAGE Arrows #-}ソース ファイルの先頭に含める必要があります)。これで十分かどうかは、DSL のニーズによって異なります。

于 2011-06-26T09:59:56.847 に答える