[英]Composing lenses with Control.Lens where an intermediate function must be called
[英]How do I correctly format a function using Control.Lens?
我目前正在通過使用鏡頭庫編寫一些簡單的函數來學習鏡頭庫。 不幸的是,我對生成的編譯器錯誤感到困惑,因此我在努力確定為什么在下面的函數dmg
中,前兩個函數可以正確編譯,而最后一個卻失敗。
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
我的問題分為三個部分。
為什么第三個dmg函數無法編譯?
我怎么寫dmg仍然使用(%~)
運算符,而不使用(&)
並仍然編譯?
什么是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>
因為解析規則。 您的代碼格式如下
abc = def %~ ghi jkl
函數應用程序的綁定比任何infix運算符都更緊密,因此將其解析為
abc = def %~ (ghi jkl)
即其他語言會寫def %~ ghi(jkl)
。
您需要(def %~ ghi) jkl
。 這通常在Haskell中使用$
完成,即
dmg myCard otherCard = health %~ ((-) (otherCard^.damage)) $ myCard
首先,我將消除不必要的括號。 運算符部分通常比使用infix的表達式好,即
dmg myCard otherCard = health %~ ((otherCard^.damage) -) $ myCard
...由於以下原因可以省略內部括號
Prelude> :info Control.Lens.^. ... infixl 8 Control.Lens.Getter.^. Prelude> :i - ... infixl 6 -
即^.
綁定比-
更加緊密
dmg myCard otherCard = health %~ (otherCard^.damage -) $ myCard
接下來,我嘗試η減少。 如果參數被交換,這將很容易,這可能是更Haskell慣用的參數順序:
dmg otherCard myCard = health %~ (otherCard^.damage -) $ myCard dmg otherCard = health %~ (otherCard^.damage -)
那可能是最優雅的解決方案。
也就是說,假設您的代碼實際上是正確的。 我不知道dmg
應該做什么,但也許更常見的情況是您想從自己的牌中減去另一張牌的傷害。 即基本上不是(otherCard^.damage -)
而是(- otherCard^.damage)
,除了將其解析為一元減號之外,因此需要寫成subtract (otherCard^.damage)
。 Lens有專門的運算符用於加法和減法,為您提供
dmg otherCard = health -~ otherCard^.damage
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.