我正在尝试习惯lensHaskell 的库,发现自己在一些简单的问题上苦苦挣扎.例如,让我们说(为了方便起见)at并且_1具有以下类型(这至少是我理解它们的方式):
at :: Ord k => k -> Lens' (Map k v) (Maybe v)
_1 :: Lens' (a, b) a
Run Code Online (Sandbox Code Playgroud)
如何将这些镜头组合成以下类型的镜头:
maybeFst :: Ord k => k -> Lens' (Map k (a, b)) (Maybe a)
Run Code Online (Sandbox Code Playgroud) 镜头库中是否有这样的快捷方式?
\x -> liftM (^. x) get
Run Code Online (Sandbox Code Playgroud)
也许这是一个愚蠢的问题,但它感觉就像一个基本的结构,应该有一个捷径.
我无法理解Haskell中镜头库的所有细微差别.
假设我有以下镜头
activePlayer :: Lens' Game Player
activePlayer = lens get set
where
get (Game {_players = (index, seq) }) = S.index seq index
set g@(Game {_players = (index, seq) }) player =
g { _players = (index, S.update index player seq) }
Run Code Online (Sandbox Code Playgroud)
在ghci提示符下执行以下操作没有问题:
> :t do{use (activePlayer); activePlayer.= undefined}
:: Control.Monad.State.Class.MonadState Game m => m ()
Run Code Online (Sandbox Code Playgroud)
但是,当我尝试将其参数化为函数时,我得到以下错误.
> :t \p -> do{use p; p.=undefined}
<interactive>:1:17:
Couldn't match type `Accessor a0 a0' with `Mutator b0'
Expected type: ASetter s0 s0 …Run Code Online (Sandbox Code Playgroud) 许多镜头吸气剂返回Maybe值.我经常需要用默认值替换它们.
说地图查找,但默认.
fromMaybe "" $ Map.fromList [(1,"Foo")] ^? at 1
Run Code Online (Sandbox Code Playgroud)
这可以用镜头语法编写吗?也许接近这个:
Map.fromList [(1,"Foo")] ^? at 1.or ""
Run Code Online (Sandbox Code Playgroud) 我一直在研究本文中给出的用于创建镜头的示例.
我Lens按照文章中的说明创建了以下代码:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
type Degrees = Double
type Latitude = Degrees
type Longitude = Degrees
data Meetup = Meetup { _name :: String, _location :: (Latitude, Longitude) }
makeLenses ''Meetup
meetupLat = location._1 :: Lens' Meetup Latitude
Run Code Online (Sandbox Code Playgroud)
现在这个代码没有类型检查,除非我包含这个:
{-# LANGUAGE NoMonomorphismRestriction #-}
Run Code Online (Sandbox Code Playgroud)
但在文章中没有任何地方,我可以发现他们已经提到了单态限制.这是正常的事情还是我在这里做错了什么?
编译使用:GHC 7.6.2
我喜欢镜头库,我喜欢它的工作方式,但有时它引入了很多问题,我后悔自己开始使用它.让我们看看这个简单的例子:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
data Data = A { _x :: String, _y :: String }
| B { _x :: String }
makeLenses ''Data
main = do
let b = B "x"
print $ view y b
Run Code Online (Sandbox Code Playgroud)
它输出:
""
Run Code Online (Sandbox Code Playgroud)
现在想象 - 我们有一个数据类型,我们通过改变一些名称来重构它.这个名称不再适用于特定的数据构造函数,而是获得错误(在运行时,与普通访问器一样),镜头使用memptyfrom Monoid来创建默认对象,因此我们得到奇怪的结果而不是错误.调试这样的东西几乎是不可能的.有没有办法解决这个问题?我知道有一些特殊的操作员可以获得我想要的行为,但是所有来自镜头的"正常"外观功能都非常糟糕.我应该用我的自定义模块覆盖它们还是有更好的方法?
作为旁注:我希望能够使用镜头语法读取和设置参数,但只删除字段缺失时自动结果创建的行为.
受到ADT之间多态函数问题的启发,我试图在多个(不仅仅是2个)类型之间创建同构,因此每次我需要一个同构但不是同一类型时,我可以将一些代码洒在我的代码上convert.
假设我有3个ADT:
data AB = A | B deriving (Show)
data CD = C | D deriving (Show)
data EF = E | F deriving (Show)
Run Code Online (Sandbox Code Playgroud)
使用lens我可以实现AB和CD,CD和EF之间的2个同构:
{-# LANGUAGE MultiParamTypeClasses #-}
class Isomorphic a b where
convert :: Iso' a b
instance Isomorphic AB CD where
convert = iso ab2cd cd2ab
where ab2cd A = C
ab2cd B = D
cd2ab C = A
cd2ab D = B
instance Isomorphic AB EF where
convert …Run Code Online (Sandbox Code Playgroud) Run Code Online (Sandbox Code Playgroud)type Traversal' a b = forall f . Applicative f => (b -> f b) -> (a -> f a) type Lens' a b = forall f . Functor f => (b -> f b) -> (a -> f a)请注意,Lens' 和 Traversal' 之间的唯一区别是类型类约束。“镜头”具有函子约束,“遍历”具有应用约束。这意味着任何 Lens' 也自动成为有效的 Traversal'(因为 Functor 是 Applicative 的超类)。
我只是不遵循这里的逻辑顺序。我们知道每个Applicative都是一个Functor。由此看来,如果有的话,难道不应该得出每个Traversal'都是 a 吗Lens'?然而,本教程得出了相反的结论。
我有一个Traversable漏洞 - 想象一下这个二叉树:
/ \
/ \ Nothing
Just 1 / \
Nothing Just 3
Run Code Online (Sandbox Code Playgroud)
我还有一个值列表来填补 - [2, 4]- 导致:
/ \
/ \ Just 4
Just 1 / \
Just 2 Just 3
Run Code Online (Sandbox Code Playgroud)
我认为可以使用lens索引遍历遍历Nothings并用列表中相应索引处的值替换它们.
但是必须可以在不使用指数的情况下直接做更多的事情吗?
奖励积分 - 此主题的一些变化:
Maybe.[2, 4, 6],[2, 4, ..]等等.我想过滤遍历,然后选择要使用的最后一个元素over.
像这样的东西(但实际上会编译):
[1,2,3,4] & traverse . filtered even . _last +~ 10
> [1,2,3,14]
Run Code Online (Sandbox Code Playgroud)
有任何想法吗?
PS我知道filtered只有在不影响遍历中的元素数量时才有效.
我正在执行的实际用例是仅选择uniplate与某个谓词匹配的递归遍历的最低级别; 如果你对如何做到这一点有其他想法,我很乐意听到它们!