将镜头传递给功能 [英] Pass a lens into a funciton

查看:152
本文介绍了将镜头传递给功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何将镜头正确传递到具有状态的功能?让我们考虑下一个代码:

  { - #LANGUAGE TemplateHaskell# - } 
{ - #LANGUAGE FlexibleContexts# - }

import Control.Lens
import Control.Monad.State

data Game = Game {_armies :: [Army]
} deriving(Show )

data Army = Army {_troops :: Int
}派生(显示)

makeLenses''游戏
makeLenses''Army

数据BattleResult = Win |失败派生(显示)

犯罪者defender = do
只是ot< - 预先使用$ offender.troops
Just dt< - 预先使用$ defender.troops
defender.troops。= 0 - 不起作用
让eval ab
| a> = b =返回Win
|否则=返回击败
eval ot dt

游戏::状态游戏()
游戏=做
军队%=(:)(军队100)$ b $ (armies.ix 0)(armies.ix 1)
return()
q < - 冒犯/ pre>

标记的行会导致下一个错误:

  Lens.hs:21:3:
无法匹配类型'Const(Data.Monoid.First Int)s'
'Identity s'
预期类型:(陆军 - > Const(Data.Monoid.First Int)Army)
- > s - >身份s
实际类型:(军队 - >常量(Data.Monoid.First Int)陆军)
- > s - > Const(Data.Monoid.First Int)s
相关绑定包括
defender ::(Army - > Const(Data.Monoid.First Int)Army)
- > s - > Const(Data.Monoid.First Int)s
(绑定在Lens.hs:18:17)
offender ::(Army - > Const(Data.Monoid.First Int)Army)
- > s - > Const(Data.Monoid.First Int)s
(绑定在Lens.hs:18:8)
offend ::((Army - > Const(Data.Monoid.First Int)Army)
- > s - > Const(Data.Monoid.First Int)s)
- > ((军队 - > Const(Data.Monoid.First Int)军队)
- > s - > Const(Data.Monoid.First Int)s)
- > m。BattleResult
(绑定在Lens.hs:18:1)
在'(。)'的第一个参数中,即'defender'
在'(。=)的第一个参数中, ',即'后卫。部队'

Lens.hs:21:12:
无法将类型'Identity Integer'
与'Const(Data.Monoid.First Int)Int'
预期类型:(Int - >身份整数)
- >军队 - > Const(Data.Monoid.First Int)Army
实际类型:(Int - > Const(Data.Monoid.First Int)Int)
- >军队 - > Const(Data.Monoid.First Int)Army
在'(。)'的第二个参数中,即'部队'
在'(。=)'的第一个参数中,即'defender。部队'

如果用类似于的方式替换该行armies.ix 0.troops 。= 0 通常编译代码。是否有一些标准工具可以解决问题?如果不使用 FlexibleContexts

解决方案

em>只需使用类型签名!



这里发生了什么:如果您不提供签名,GHC将只能推断Rank- 1型。在本例中,您使用 defender.troops 作为getter ;编译器因此推断出 defender 的getter类型。这是错误消息中带有 Const 的丑陋之处。



但是,您也想使用它作为二传手。这只有在 defender 是多态时才是可能的(所以你可以使用 Identity functor而不是 Const ),并且对于一个参数是多态的,你需要Rank-2多态。

你并不需要担心这种类型但是,由于镜头库提供了易于使用的同义词,因此它也属于类别理论魔术。 ,



 冒犯:: Traversal'游戏军团 - >穿越'游戏军团 - > State GameResultResult 

,您将得到正确的多态参数。啊,当然你需要 -XRankNTypes 扩展名。 -XFlexibleContexts 实际上并不是必需的(尽管这是完全无害的,没有理由不使用它)。




如果你问我,Hindley-Milner反正是一个奇迹,但它只是起作用,因为有一个定义明确的可能的任何表达式的签名。 Rank-1代码只有这种情况:在Rank-N中,您可以随时在另一层普遍量化中折腾。编译器无法知道何时结束!
$ b 实际上,一个获取,这是一个遍历getter。 Getter 获得之间的区别在于后者可能是部分的(这是必要的,因为您使用 ix ;编译器无法证明在军队列表中的索引1处实际上有一个元素)


How to pass properly a lens into a function with state? Let us consider the next code:

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleContexts #-}

import Control.Lens
import Control.Monad.State

data Game = Game { _armies :: [Army]
                 } deriving (Show)

data Army = Army { _troops :: Int
                 } deriving (Show)

makeLenses ''Game
makeLenses ''Army

data BattleResult = Win | Defeat deriving (Show)

offend offender defender = do
  Just ot <- preuse $ offender.troops
  Just dt <- preuse $ defender.troops
  defender.troops.=0 -- doesn't work
  let eval a b
        | a >= b    = return Win
        | otherwise = return Defeat
  eval ot dt

game :: State Game ()
game = do
    armies %= (:) (Army 100)
    armies %= (:) (Army 200)
    q <- offend (armies.ix 0) (armies.ix 1)
    return ()

The marked line leads to the next error:

Lens.hs:21:3:
    Couldn't match type ‘Const (Data.Monoid.First Int) s’
                   with ‘Identity s’
    Expected type: (Army -> Const (Data.Monoid.First Int) Army)
                   -> s -> Identity s
      Actual type: (Army -> Const (Data.Monoid.First Int) Army)
                   -> s -> Const (Data.Monoid.First Int) s
    Relevant bindings include
      defender :: (Army -> Const (Data.Monoid.First Int) Army)
                  -> s -> Const (Data.Monoid.First Int) s
        (bound at Lens.hs:18:17)
      offender :: (Army -> Const (Data.Monoid.First Int) Army)
                  -> s -> Const (Data.Monoid.First Int) s
        (bound at Lens.hs:18:8)
      offend :: ((Army -> Const (Data.Monoid.First Int) Army)
                 -> s -> Const (Data.Monoid.First Int) s)
                -> ((Army -> Const (Data.Monoid.First Int) Army)
                    -> s -> Const (Data.Monoid.First Int) s)
                -> m BattleResult
        (bound at Lens.hs:18:1)
    In the first argument of ‘(.)’, namely ‘defender’
    In the first argument of ‘(.=)’, namely ‘defender . troops’

Lens.hs:21:12:
    Couldn't match type ‘Identity Integer’
                   with ‘Const (Data.Monoid.First Int) Int’
    Expected type: (Int -> Identity Integer)
                   -> Army -> Const (Data.Monoid.First Int) Army
      Actual type: (Int -> Const (Data.Monoid.First Int) Int)
                   -> Army -> Const (Data.Monoid.First Int) Army
    In the second argument of ‘(.)’, namely ‘troops’
    In the first argument of ‘(.=)’, namely ‘defender . troops’

If replace the line with something like armies.ix 0.troops.=0 the code is normally compiled. Are there some standard tools to walk around the problem? And could the same algorithm be implemented without using FlexibleContexts?

解决方案

Just use type signatures!

What's going on here: if you don't supply a signature, GHC will only be able to infer a Rank-1 type. In this example, you're using defender.troops as a getter; the compiler therefore infers a getter type for defender. This is the ugly thing in the error message with Const in it.

However, you also want to use it as a setter. This is only possible if defender is polymorphic (so you can use an Identity functor instead of Const), and for an argument to be polymorphic you need Rank-2 polymorphism.

You don't really need to worry about this kind of category-theory magic though, since the lens library supplies easy to use synonyms. Just write the signature as you should always do anyway,

offend :: Traversal' Game Army -> Traversal' Game Army -> State Game BattleResult

and you get the proper polymorphic arguments. Ah, and of course you need the -XRankNTypes extension. -XFlexibleContexts is actually not required (it is completely harmless though, no reason not to use it).


Hindley-Milner is quite a marvel anyway if you ask me, but it only works because there's a well-defined most general possible signature for any expression. This is only the case for Rank-1 code though: with Rank-N, you could always toss in yet another layer of universal quantification. The compiler can't know when to end this!

Actually a Getting, which is a traversal-getter. The difference beteween Getter and Getting is that the latter can be partial (which is necessary because you use ix; the compiler can't prove there is actually an element at index 1 in the armies list).

这篇关于将镜头传递给功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆