2017-06-02 8 views
0

私は現在、ライブラリを使って簡単な関数を書くことによってレンズライブラリを学習しています。残念なことに、私はコンパイラのエラーが生成されて混乱しているので、以下の関数dmgの最初の2つの関数は正しくコンパイルされますが、最後は失敗するのはなぜですか?Control.Lensを使用して関数を正しくフォーマットするにはどうすればよいですか?

import Control.Lens 

type Health = Int 
type Damage = Int 

data Card = Card { 
    _health :: Int, 
    _damage :: Int 
} deriving (Show,Eq) 

health :: Lens' Card Health 
health = lens _health (\card h -> card { _health = h }) 
damage :: Lens' Card Damage 
damage = lens _damage (\card d -> card { _damage = d }) 

cardDead :: Card -> Bool 
cardDead c = c^.health <= 0 

duel :: (Card,Card) -> (Card,Card) 
duel (c,c2) = ((dmg c c2),(dmg c2 c)) 

本物の肉です。

dmg :: Card -> Card -> Card 
dmg myCard otherCard = over health ((-) (otherCard^.damage)) myCard --compiles 
dmg myCard otherCard = myCard & health %~ ((-) (otherCard^.damage)) --compiles 
dmg myCard otherCard = health %~ ((-) (otherCard^.damage)) myCard --compile error 

私の質問は3つの部分に分かれています。

  1. なぜ3番目のdmg関数がコンパイルに失敗しますか?

  2. (%~)演算子をまだ使用するにはどうすればいいですか?(&)は使用せず、まだコンパイルしますか?

  3. dmgを書くのに最も美しく、慣用的なレンズの方法は何ですか?

- 参考

は、ここにあなたがレンズなしのdmgを書くでしょう可能性が一つの方法である

dmg myCard otherCard = 
    let 
     damageTaken = _damage otherCard 
     oldHealth = _health myCard 
     newHealth = oldHealth - damageTaken 
    in myCard {_health = newHealth} 

編集:参考のために、ここで私はトラブルに理解を持っていたエラーメッセージがあります(間違って書かれた)ライン3.

*Main GHC.Arr Control.Applicative Control.Lens> :l Doom.hs 
[1 of 1] Compiling Main    (Doom.hs, interpreted) 

Doom.hs:26:24: 
    Couldn't match expected type `Card' with actual type `Card -> Card' 
    In the expression: health %~ ((-) (otherCard ^. damage)) myCard 
    In an equation for `dmg': 
     dmg myCard otherCard = health %~ ((-) (otherCard ^. damage)) myCard 

Doom.hs:26:51: 
    Couldn't match type `Health -> Health' with `Int' 
    Expected type: Getting (Health -> Health) Card (Health -> Health) 
     Actual type: (Damage -> Const (Health -> Health) Damage) 
        -> Card -> Const (Health -> Health) Card 
    In the second argument of `(^.)', namely `damage' 
    In the first argument of `(-)', namely `(otherCard ^. damage)' 

Doom.hs:26:60: 
    Couldn't match expected type `Health -> Health' 
       with actual type `Card' 
    In the second argument of `(-)', namely `myCard' 
    In the second argument of `(%~)', namely 
     `((-) (otherCard ^. damage)) myCard' 
Failed, modules loaded: none. 
Prelude GHC.Arr Control.Applicative Control.Lens> 
+1

"混乱しているコンパイルエラー"については、実際にそれらを含めるべきです。 – Carl

+0

良い点。以下の答えはかなり完璧でしたが、後世のために私がP3をコンパイルしている間違いが含まれています。 – SolventGren

答えて

3
  1. 構文解析のルール。あなたのコードは

    abc = def %~ ghi jkl 
    

    機能アプリケーションは、任意の中置演算子より緊密な結合する形態であるので、これは他の言語がdef %~ ghi(jkl)を書くでしょう、すなわち何

    abc = def %~ (ghi jkl) 
    

    として解析されます。

  2. 代わりに(def %~ ghi) jklが必要です。これは一般的に、すなわち

    dmg myCard otherCard = health %~ ((-) (otherCard^.damage)) $ myCard 
    
  3. まず、私は不要な括弧をなくしたい、Haskellで$で達成されます。オペレータのセクションでは、通常^.が与え、とにかくより緊密に-以上結合し、つまり内側の括弧が原因

    Prelude> :info Control.Lens.^. 
    ... 
    infixl 8 Control.Lens.Getter.^. 
    Prelude> :i - 
    ... 
    infixl 6 - 
    

    に省略することができる、すなわち

    dmg myCard otherCard = health %~ ((otherCard^.damage) -) $ myCard 
    

    ...中置適用-に表現よりも立派です

    dmg myCard otherCard = health %~ (otherCard^.damage -) $ myCard 
    

    次に、私はη-reductionを試みます。

    dmg otherCard myCard = health %~ (otherCard^.damage -) $ myCard 
    dmg otherCard = health %~ (otherCard^.damage -) 
    

    そして、それはおそらく、最もエレガントなソリューションです:引数はおそらくよりハスケル・慣用引数の順序である、スワップされる場合、これは簡単だろう。

つまり、コードが実際に正しいと仮定します。私はdmgが何をすべきかはわかりませんが、おそらくより一般的なのは、あなたが他のカードのダメージをあなたのものから差し引きたい場合です。私。基本的には(otherCard^.damage -)ではなく(- otherCard^.damage)ではありませんが、単項マイナスとして解析されるため、subtract (otherCard^.damage)と書く必要があります。レンズには加算と減算の専用の演算子があります。

dmg otherCard = health -~ otherCard^.damage 
+0

ありがとう、素晴らしい答え。 – SolventGren

関連する問題