如何使用 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
我的问题分为三个部分。
为什么第三个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
函数应用程序比任何中缀运算符绑定得更紧密,因此它被解析为
abc = def %~ (ghi jkl)
即其他语言会写什么 def %~ ghi(jkl)
.
您需要 (def %~ ghi) jkl
。这通常通过 Haskell 中的 $
来完成,即
dmg myCard otherCard = health %~ ((-) (otherCard^.damage)) $ myCard
首先我会去掉不必要的括号。运算符部分通常比应用中缀的表达式更好,即
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
应该做什么,但也许更常见的情况是你想从你的伤害中减去另一张卡的伤害。 IE。基本上不是 (otherCard^.damage -)
,而是 (- otherCard^.damage)
,除非它被解析为一元减号,因此需要写成 subtract (otherCard^.damage)
。 Lens 有专门的加减运算符,给你
dmg otherCard = health -~ otherCard^.damage
我目前正在通过使用库编写一些简单的函数来学习镜头库。不幸的是,我对生成的编译器错误感到很困惑,所以我很难确定为什么在下面的函数 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
函数应用程序比任何中缀运算符绑定得更紧密,因此它被解析为
abc = def %~ (ghi jkl)
即其他语言会写什么
def %~ ghi(jkl)
.您需要
(def %~ ghi) jkl
。这通常通过 Haskell 中的$
来完成,即dmg myCard otherCard = health %~ ((-) (otherCard^.damage)) $ myCard
首先我会去掉不必要的括号。运算符部分通常比应用中缀的表达式更好,即
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
应该做什么,但也许更常见的情况是你想从你的伤害中减去另一张卡的伤害。 IE。基本上不是 (otherCard^.damage -)
,而是 (- otherCard^.damage)
,除非它被解析为一元减号,因此需要写成 subtract (otherCard^.damage)
。 Lens 有专门的加减运算符,给你
dmg otherCard = health -~ otherCard^.damage