复合镜头不能让绑定吗?
Is it impossible to let-bind a composite lens?
通过帮助类型检查器,我无法理解以下内容是否可行,或者完全不可能。
设置有点随意,我只需要一些带有镜头的嵌套数据类型,这里称为 A
、B
、C
.
我的问题是我可以使用复合镜头 (bLens . a)
如果我立即使用它调用 view
之类的东西,但是如果我尝试让绑定它并给它一个名字,我获取错误消息。
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
module Debug where
import Control.Eff
import Control.Eff.Reader.Strict
import Control.Lens
data A = A
data B = B
{ _a :: A
}
makeLenses ''B
data C = C
{ _b :: B
}
makeLenses ''C
askLensed :: ( Member (Reader r) e ) => Lens' r a -> Eff e a
askLensed l = view l <$> ask
works :: ( Member (Reader C) e ) => Lens' C B -> Eff e A
works bLens = do
askLensed (bLens . a)
doesNotWork :: ( Member (Reader C) e ) => Lens' C B -> Eff e A
doesNotWork bLens = do
let compositeLens = bLens . a
askLensed compositeLens
doesNotWork2 :: ( Member (Reader C) e ) => Lens' C B -> Eff e A
doesNotWork2 bLens = do
let compositeLens :: Lens' C A = bLens . a
askLensed compositeLens
butThisIsFine :: Lens' C B -> Lens' C A
butThisIsFine bLens =
let compositeLens = bLens . a in compositeLens
错误信息是:
• Could not deduce (Functor f0) arising from a use of ‘bLens’
from the context: Member (Reader C) e
bound by the type signature for:
doesNotWork :: forall (e :: [* -> *]).
Member (Reader C) e =>
Lens' C B -> Eff e A
at /home/.../.stack-work/intero/interoW51bOk-TEMP.hs:32:1-62
The type variable ‘f0’ is ambiguous
Relevant bindings include
compositeLens :: (A -> f0 A) -> C -> f0 C
和:
• Couldn't match type ‘f0’ with ‘f’
because type variable ‘f’ would escape its scope
This (rigid, skolem) type variable is bound by
a type expected by the context:
Lens' C A
at /home/.../.stack-work/intero/interoW51bOk-TEMP.hs:35:3-25
Expected type: (A -> f A) -> C -> f C
Actual type: (A -> f0 A) -> C -> f0 C
• In the first argument of ‘askLensed’, namely ‘compositeLens’
In a stmt of a 'do' block: askLensed compositeLens
In the expression:
do let compositeLens = bLens . a
askLensed compositeLens
• Relevant bindings include
compositeLens :: (A -> f0 A) -> C -> f0 C
我尝试添加类型签名,并明确量化 f
和 Functor
约束,但到目前为止没有成功。
一个简化许多事情的经验法则是不要将 Lens
(或 Lens'
,或 Setter
等)作为函数参数,除非你真的需要它多态性,而是采用 ALens
(或 ALens'
或 ASetter
)版本,这避免了 Rank-2 多态性问题。
问题是,如果您在 do
块中给 compositeLens
一个名称,那么它必须具有无法再从其上下文中推断出的类型。但是 Lens' C A
在底层是一个多态类型,这使类型推断变得相当复杂。 是如果你给一个明确的签名其实没问题:
doesActuallyWork2 :: ( Member (Reader C) e ) => Lens' C B -> Eff e A
doesActuallyWork2 bLens = do
let compositeLens :: Lens' C A
compositeLens = bLens . a
askLensed compositeLens
您的版本 doesNotWork2
无效,因为带有内联签名的定义被翻转到 RHS,例如
doesNotWork2 :: ( Member (Reader C) e ) => Lens' C B -> Eff e A
doesNotWork2 bLens = do
let compositeLens = bLens . a :: Lens' C A
askLensed compositeLens
...其中 compositeLens
再次尝试将刚刚给出的类型专门化为一个特定的函子,这是无法完成的。
更直接的解决方案是完全避免这种局部多态性,您实际上并不需要:如果您将 ALens'
作为参数,局部绑定会自动采用单态类型:like
worksEasily :: ( Member (Reader C) e ) => ALens' C B -> Eff e A
worksEasily bLens = do
let compositeLens = bLens . a
askLensed compositeLens
其实不完全是这样;原来你不想要 ALens'
这里而是 Getting
。找到它的最简单方法是删除 askLensed
的签名并让编译器告诉您它推断的内容,然后从中向后工作。
askLensed :: ( Member (Reader r) e ) => Getting a r a -> Eff e a
askLensed l = view l <$> ask
worksEasily :: ( Member (Reader r) e ) => Getting A r B -> Eff e A
worksEasily bLens = do
let compositeLens = bLens . a
askLensed compositeLens
通过帮助类型检查器,我无法理解以下内容是否可行,或者完全不可能。
设置有点随意,我只需要一些带有镜头的嵌套数据类型,这里称为 A
、B
、C
.
我的问题是我可以使用复合镜头 (bLens . a)
如果我立即使用它调用 view
之类的东西,但是如果我尝试让绑定它并给它一个名字,我获取错误消息。
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
module Debug where
import Control.Eff
import Control.Eff.Reader.Strict
import Control.Lens
data A = A
data B = B
{ _a :: A
}
makeLenses ''B
data C = C
{ _b :: B
}
makeLenses ''C
askLensed :: ( Member (Reader r) e ) => Lens' r a -> Eff e a
askLensed l = view l <$> ask
works :: ( Member (Reader C) e ) => Lens' C B -> Eff e A
works bLens = do
askLensed (bLens . a)
doesNotWork :: ( Member (Reader C) e ) => Lens' C B -> Eff e A
doesNotWork bLens = do
let compositeLens = bLens . a
askLensed compositeLens
doesNotWork2 :: ( Member (Reader C) e ) => Lens' C B -> Eff e A
doesNotWork2 bLens = do
let compositeLens :: Lens' C A = bLens . a
askLensed compositeLens
butThisIsFine :: Lens' C B -> Lens' C A
butThisIsFine bLens =
let compositeLens = bLens . a in compositeLens
错误信息是:
• Could not deduce (Functor f0) arising from a use of ‘bLens’
from the context: Member (Reader C) e
bound by the type signature for:
doesNotWork :: forall (e :: [* -> *]).
Member (Reader C) e =>
Lens' C B -> Eff e A
at /home/.../.stack-work/intero/interoW51bOk-TEMP.hs:32:1-62
The type variable ‘f0’ is ambiguous
Relevant bindings include
compositeLens :: (A -> f0 A) -> C -> f0 C
和:
• Couldn't match type ‘f0’ with ‘f’
because type variable ‘f’ would escape its scope
This (rigid, skolem) type variable is bound by
a type expected by the context:
Lens' C A
at /home/.../.stack-work/intero/interoW51bOk-TEMP.hs:35:3-25
Expected type: (A -> f A) -> C -> f C
Actual type: (A -> f0 A) -> C -> f0 C
• In the first argument of ‘askLensed’, namely ‘compositeLens’
In a stmt of a 'do' block: askLensed compositeLens
In the expression:
do let compositeLens = bLens . a
askLensed compositeLens
• Relevant bindings include
compositeLens :: (A -> f0 A) -> C -> f0 C
我尝试添加类型签名,并明确量化 f
和 Functor
约束,但到目前为止没有成功。
一个简化许多事情的经验法则是不要将 Lens
(或 Lens'
,或 Setter
等)作为函数参数,除非你真的需要它多态性,而是采用 ALens
(或 ALens'
或 ASetter
)版本,这避免了 Rank-2 多态性问题。
问题是,如果您在 do
块中给 compositeLens
一个名称,那么它必须具有无法再从其上下文中推断出的类型。但是 Lens' C A
在底层是一个多态类型,这使类型推断变得相当复杂。 是如果你给一个明确的签名其实没问题:
doesActuallyWork2 :: ( Member (Reader C) e ) => Lens' C B -> Eff e A
doesActuallyWork2 bLens = do
let compositeLens :: Lens' C A
compositeLens = bLens . a
askLensed compositeLens
您的版本 doesNotWork2
无效,因为带有内联签名的定义被翻转到 RHS,例如
doesNotWork2 :: ( Member (Reader C) e ) => Lens' C B -> Eff e A
doesNotWork2 bLens = do
let compositeLens = bLens . a :: Lens' C A
askLensed compositeLens
...其中 compositeLens
再次尝试将刚刚给出的类型专门化为一个特定的函子,这是无法完成的。
更直接的解决方案是完全避免这种局部多态性,您实际上并不需要:如果您将 ALens'
作为参数,局部绑定会自动采用单态类型:like
worksEasily :: ( Member (Reader C) e ) => ALens' C B -> Eff e A
worksEasily bLens = do
let compositeLens = bLens . a
askLensed compositeLens
其实不完全是这样;原来你不想要 ALens'
这里而是 Getting
。找到它的最简单方法是删除 askLensed
的签名并让编译器告诉您它推断的内容,然后从中向后工作。
askLensed :: ( Member (Reader r) e ) => Getting a r a -> Eff e a
askLensed l = view l <$> ask
worksEasily :: ( Member (Reader r) e ) => Getting A r B -> Eff e A
worksEasily bLens = do
let compositeLens = bLens . a
askLensed compositeLens