4

拡張された解析機能を備えたライブラリを実装しようとしています。大学で知っていたので、fsyaccを使うことにしました。残念ながら、次の問題が発生しました。

文法の先頭(Head)のクラスを定義し、その実装を1つのファイルに配置しました。次に、パーサーを次のように定義しました。

...
%start head
%type <Head> head
...

Fsyaccは、分離されたモジュール(パーサー)を生成します。成功するには、次の順序でコンパイルする必要があります。Head.fs Parser.fs

このライブラリを.NETにあるものと同様にするために、静的な解析メソッドをHeadに追加したいと思います。残念ながら、Parserモジュールのメソッドを使用する必要があります。

このような型の依存関係は'および'演算子で解決できることは知っていますが、1つのファイルで定義された型にのみ適用できます。

別々のファイルにある場合でも、相互に依存する型を作成する他の方法はありますか?C / C ++のような宣言/実装分離メカニズムを探していましたが、何も見つかりませんでした。

4

4 に答える 4

7

簡単な答え:いいえ。F#2.0では、複数のファイル間で相互再帰エンティティを実行する方法はありません。(これは、言語の次のバージョンで対処する予定です。)

これはさまざまな方法で回避できます。通常は、間接参照と変更のポイントを使用します。たとえば、Headタイプには、関数値を可変グローバル変数にポークする静的な'InitializeParser'メソッドがあり、Headで定義された静的Parseメソッドは、その可変グローバルを介して呼び出すことができ、パーサーが実際に定義された後、 InitializeParserを呼び出して値を突き刺すことができます(それが意味をなさない場合は、より詳細に説明できます)。

于 2010-10-21T01:25:07.513 に答える
3

それが可能だと思っていました。ブライアンの返信を読んだ後、適切な回避策を探し始めました。ライブラリユーザーに初期化メソッドの呼び出しを強制したくありませんでした。したがって、私は何か違うものを思いついた。

コンパイラがコンパイル時に依存関係を解決できない場合は、実行時に自分で解決できます。これが私のDepencendciesResolverの定義です

module DependenciesResolver = 
    let GetMethod(_type, _method) =
        lazy (
            let a = System.Reflection.Assembly.GetExecutingAssembly()
            let t = a.GetType(_type)
            t.GetMethod(_method)
            )

そして、別々のファイルで定義されたクラスの例:

A.fs

namespace MutualRecursion
type A() =
    static member _b = DependenciesResolver.GetMethod("MutualRecursion.B", "b")
    static member b() = A._b.Value.Invoke(null, [||])

B.fs

nameespace MutualRecursion
type B =
    static member b() = 
        printf "Called b()"

コンパイル順序は次のとおりです。A.fsB.fs

于 2010-10-21T10:18:29.917 に答える
1

これら2つの後にコンパイルされ、新しいメソッドでHeadを拡張する3番目のファイルでこれを回避できませんか?

于 2010-10-21T08:26:18.573 に答える
1

私は次のようなことをします(これは大まかにブライアンが提案していたことだと思います)。ユーザーはトリッキーな初期化を行う必要がないことに注意してください。タイプ自体が「結び目を結ぶ」方法を知っています。

Head.fs

type IParser =
  abstract Parse : string -> int // or whatever
  ...

type Head() =
  static let mutable parser = Unchecked.defaultof<IParser>
  static member internal SetParser p = parser <- p
  member x.DoSomethingCool() =
    let i = parser.Parse("something to parse")
    ...

Parser.fs

type Parser private () =
  static do
    Head.SetParser (Parser())
  interface IParser with
    member x.Parse s = 0
    ...
于 2010-10-21T15:50:33.723 に答える