簡體   English   中英

如何使用Control.Lens正確格式化函數?

[英]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

我的問題分為三個部分。

  1. 為什么第三個dmg函數無法編譯?

  2. 我怎么寫dmg仍然使用(%~)運算符,而不使用(&)並仍然編譯?

  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. 因為解析規則。 您的代碼格式如下

     abc = def %~ ghi jkl 

    函數應用程序的綁定比任何infix運算符都更緊密,因此將其解析為

     abc = def %~ (ghi jkl) 

    即其他語言會寫def %~ ghi(jkl)

  2. 您需要(def %~ ghi) jkl 這通常在Haskell中使用$完成,即

     dmg myCard otherCard = health %~ ((-) (otherCard^.damage)) $ myCard 
  3. 首先,我將消除不必要的括號。 運算符部分通常比使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM