2016-12-20 16 views
3

私はKmettのレンズに絶えず深く潜っています。今日私はいくつかのカスタムトラバーサルを作成しようとしています。今までは、既存のトラバーサルを作成して新しいトラバースを作成することに成功しましたが、もう少し複雑な作業をしています。より複雑なトラバーサル(レンズ)を書く

私はテキストエディタを作成しています。複数のカーソルを追加していますが、最初は各バッファに1つのカーソルがあり、フォーカスするレンズがありました。今はカーソルのリストに一般化しています。リスト。トリックは前のケースでは私のレンズがセッター内でいくつかのバリデーションを行い、カーソルがバッファのテキストの有効範囲内に収まるようにしていたということです。

clampCursor :: Text -> Cursor -> Cursor 

cursor :: Lens' Buffer Cursor 
cursor = lens getter setter 
    where getter buf = buf^.curs 
     setter buf new = let txt = buf^.text 
          in buf & curs .~ clampCursor txt new 

バッファーのコンテキストからのテキスト情報を使用してカーソル上にレンズを作成する方法に注意してください。 (誰かが提案をしている場合はカスタムレンズを作る代わりにこれを行うためのクリーンな方法について聞いてみたいと思っています。

これで複数のカーソルが得られました。これをトラバーサルに変換する必要がありますが、もちろんlens getter setterメソッドを使用してトラバーサルを定義することはできません。トラバーサルを定義する方法を見て、私はこれを読むtutorial;

質問:トラバーサルを作成するにはどうすればよいですか?

回答:

  • トラバースは、「あなたはTraversableの
  • を実装する任意のタイプのためにすべてのレンズを得るトラバーサルも型チェックしますトラバーサルの通りです:プリミティブのトラバースを作成するには、3つの主な方法があります。また、(このチュートリアルではカバーされていない)トラバーサル」で「すべてのプリズムは、以来、あなたはトラバーサルの使用makePrismsを生成するためのテンプレートHaskellのを使用することができます

これらの方法のどれも実際にここで助けません。私はアプリケーションスタイルを使ってトラバースを作成するスタイルも見てきましたが、それはいつも私にとっては少し混乱していました。私が望むものを得るためにこのケースでどのように使用するのか本当に分かりません。

私はと書いて、バリデーションを実行するためにセッターのカーソルをマップして、それからそのリストをトラバースすると仮定しますが、トラバース後にトラバーサルにベーキングする方法が必要です何らかの形で単一の要素が集中している)。おそらく、これを完全に行うより良い方法があります。

私はトラバーサルについて学べるほど多くのことを学んでいますので、説明は分かります!ありがとう!

編集: @dfeuerは、この種の検証を行うと無効なレンズになることを指摘しました。レンズ内部で行うためのクリーンなインターフェイスが本当に気に入りました。私が知っている限り、バリデーションは偶発的であるため、実際の問題は発生しないはずですが、これをどうやって改善するかについての提案があります。

+1

あなたはすでに問題のビットを持っている:私たちはTraversal'をしたいので、我々は今、私たちのfApplicative f制約を持っていることを除いて、私たちのためのマッピング」、コードが同じである、クランプのおかげで、あなたの「レンズ」は、法を守るレンズではありません。 'Cursor'が何であり、どのように使用されているのかについていくつかの情報を与えることができますか?あなたの現在の設定は私に少し奇妙に感じます。私は、カーソルをバッファの「一部」と考える傾向がありません。あなたが本当に望むかもしれないと思うのは、レンズのようなバッファーのような "ポインタ"のようなものです。 – dfeuer

+0

ええと、そうだね。インターフェイスがきれいだが法を遵守している場合、検証を行うためのより良い方法がありますか?私はいつもバリデーションをどこで実行すべきかと考えてきました。基本的には、カーソルは、ファイルへのオフセットであり、将来の操作がどこで実行されるべきかを記録する。文字の削除、テキストの挿入などが含まれます。この場合の「バッファ」は、編集可能なテキストの表現のVimの用語を指します。私の場合、各バッファにはカーソルがあります。 私はレンズのようなポインタを使ってすべてをやってみたいですが、それをしようとすると他のレンズの法則が壊れます:P –

+0

カーソルが設定されているときにカーソルのリストをさらに変換したいと思います。例えば、私はそれらを並べ替え、重複を削除したいと思います。 –

答えて

2

私の推薦は、この種のものを行うために直接レンズのFunctor/Applicative表現を使用することです。

非タイプ変更(シンプル)LensまたはTraversalを作成するには、引数として機能k :: a -> f aとあなたの構造sをとり、その後、f sを生成する関数を記述する必要があります。

kは、レンズのユーザーがレンズによって集束されたデータに加えたい変更を表す一般化された修正機能の一種です。しかし、kはタイプa -> aではなく、タイプa -> f aの代わりに、更新前のフィールドの値など、更新から「結果」を持ち出すこともできます(状態モナドを使用する場合はfであれば、更新関数でフィールドの古い値に状態を設定し、後で状態モナドを実行するときに読み出すことができます)。

次のコードでは我々のアプローチは、新しい値を返す前に、いくつかのクランプを実行するには、この変更機能を変更することです:

-- Takes a cursor update function and returns a modified update function 
-- that clamps the return value of the original function 
clampCursorUpdate :: Functor f => Text -> (Cursor -> f Cursor) -> (Cursor -> f Cursor) 
clampCursorUpdate k = \cur -> fmap (clampCursor txt) (k cur) 

私たちは、その後の検証レンズに非検証レンズを変えることができますが(ということに注意してください。コメントで言ったように、この検証レンズ)は遵法レンズではありません。

-- assuming that _cursor is a lens that accesses 
-- the _cursor field without doing any validation 
_cursor :: Lens' Buffer Cursor 

cursor :: Functor f => (Cursor -> f Cursor) -> Buffer -> f Buffer 
cursor k buffer = _cursor (clampCursorUpdate txt k) buffer 
where txt = buffer^.text 

このアプローチでは、横断に一般化することは容易です。我々が以前になかったので今、私たちは同じアプローチを使用することができます

-- assuming that _cursors is a lens that returns the list of cursors 
_cursors :: Lens' Buffer [Cursor] 

-- non-validating cursors traversal 
_cursorsTraversal :: Traversal' Buffer Cursor 
_cursorsTraversal = _cursors . traverse 

:まず、我々はTraversal' Buffer Cursorにそれを向けるだろうtraverseLens' Buffer [Cursor]、合成することにより妥当性を検証トラバーサルを書くトラバーサルがすでにないために」

cursors :: Applicative f => (Cursor -> f Cursor) -> Buffer -> f Buffer 
cursors k buffer = _cursorsTraversal (clampCursorUpdate txt k) buffer 
whee txt = buffer^.text 
0

curs :: Lens' Buffer Cursorcurs :: Lens' Buffer [Cursor]なると、法律無視cursor :: Traversal' Buffer Cursorを構築するあなたの仕事は、法律無視Lens' Buffer [Cursor]境界チェックを行い構築、およびTraversal' s aに任意のLens' s [a]を回すに分割することができます。

最初はあなたによって解決されている可能性があります:あなたはすでに行っていることをやっていますが、[Cursor]の各要素の境界を確認してください。

第二は、遵法要求であり、したがって、あなたのポストの下のコメントの急流のない答え期待している可能性が:私の楽しみのためにも

turnIntoTraversal :: Lens' s [a] -> Traversal' s a 
turnIntoTraversal l = l . traverse 

をここに直接実装する試みです。

cursor :: Traversal' Buffer Cursor 
cursor atofa s = (curs . traverse) (fmap (clampCursor $ s ^. text) . atofa) s 
関連する問題