2016-09-17 7 views
3

例として、フロントホイール&(後輪)と、直径を表すIntを持つ自転車があります。Haskell:変数に基づくレコード内のフィールドの置き換え

type Wheel = Int 
data Bike = Bike { frontwheel :: Wheel, rearwheel :: Wheel } 
    deriving (Show) 

mybike = Bike 24 26 

私は、彼らはさまざまなサイズだと好きではないので、今私は、ホイールを交換したい:

replaceFrontWheel :: Bike -> Wheel -> Bike 
replaceFrontWheel bike wheel = bike { frontwheel = wheel } 

repairedbike = replaceFrontWheel mybike 26 

作品!

しかし、フロントを交換する機能を望むなら、またはリアホイール?どちらのホイールはホイール(INT)の後に、すべてのタイプのものであり、なぜ同様のパラメータとしてフィールドを取り、単一の機能でそれをしない:それは動作しない理由を私は理解して

replaceWheel bike position wheel = bike { position = wheel } 

repairedbike = replaceWheel mybike frontwheel 26 

positionは、値がfrontwheelであると解釈されず、存在しないフィールドとしてpositionBikeと解釈されます。

(JS)mybike[position] = 26または(PHP)$mybike->$position = 26のHaskellアナログがありますか?

外部モジュールなしでエレガントにできますか?

レンズを使用することは可能ですか?

+5

これは、レンズが大部分を占めています。 – melpomene

答えて

6

はい、lensは必要なものです。あなたが望んでいただけのように使用することが

import Control.Lens 
import Control.Lens.TH 

data Bike = Bike { _frontwheel, _rearwheel :: Wheel } 
deriving (Show) 
makeLenses ''Bike 

replaceWheel :: Bike -> Lens' Bike Wheel -> Wheel -> Bike 
replaceWheel bike position wheel = bike & position .~ wheel 

repairedbike = replaceWheel mybike frontwheel 26 

あなたは署名を少し弱めることができます。

replaceWheel :: Bike -> Setter' Bike Wheel -> Wheel -> Bike 

本質的

replaceWheel :: Bike 
      -> ((Wheel->Identity Wheel) -> (Bike->Identity Bike)) 
      -> Wheel 
      -> Bike 
を言うだけの空想の方法です

だから、

data Bike = Bike { _frontwheel, _rearwheel :: Wheel } -- no lenses 

frontWheel :: (Wheel -> Wheel) -> Bike -> Bike 
frontWheel f (Bike fw rw) = Bike (f fw) rw 

repairedbike = replaceWheel mybike frontwheel 26 

確かにあなたは厳密にはありません、:は、このように呼び出すことができます

replaceWheel :: Bike -> ((Wheel->Wheel) -> Bike->Bike) -> Wheel -> Bike 
replaceWheel bike position wheel = bike & position (const wheel) 
           -- = position (const wheel) bike 

であなたを終了する、あなたにも、それを省略する可能性があるだけでタイプレベルの同型ですこれのためのライブラリが必要です!それは、このようなアドホック近似ではなく、適切なレンズを使用するpreferrableだ

理由は、次のとおりです。

  • より一般的。 Lens'は、値の設定、取得(およびトラバース)の両方に使用できます。これは、lensが使用する根本的なRank2多型がなければ、厄介にしか表現できません。
  • さらに簡潔です。上記のタイプには多くの冗長性があります。レンズはこれらのアクセサーの短い同義語を提供します。
  • 安全です。関数(Wheel -> Wheel) -> Bike -> Bikeはすべての種類のゴミを処理できます。 lensは、基本的にレンズを保証するレンズ法則を必要とします。
  • 高速です。レンズライブラリのコンビネータは、パフォーマンスを考慮して記述されています(つまり、ストリーム融合のインライン展開、状態モナドでのコピーの省略など)。 「何かを変更する」それが最後に変更される引数を置くためにHaskellで、従来だと機能のためのBTW

、:

replaceWheel :: Setter' Bike Wheel -> Wheel -> Bike -> Bike 
replaceWheel position wheel = position .~ wheel 

...または、さらに短い、

replaceWheel = (.~) 
+0

あなたの包括的な説明をありがとう! 'cabal install lens'を実行し、' { - #LANGUAGE TemplateHaskell# - } 'をファイルの先頭に追加すると、それは完全に動作します。あなたはまた私が持っていた別の質問に答えました:関数を使ってフィールドを変換する方法。私のニーズに合った '(%〜)'が出てきました! – Fx32

関連する問題