我无法通过帮助类型检查器来了解以下内容是否可行,或者根本就不可能。
设置有些随意,我只需要一些带有镜头的嵌套数据类型,这里称为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
约束,但到目前为止没有成功。
答案 0 :(得分:6)
简化很多事情的经验法则是,除非您确实需要光学多态性,否则不要将Lens
(或Lens'
或Setter
等作为函数参数,而是采用ALens
(或ALens'
或ASetter
)版本,这样可以避免Rank-2多态性问题。
问题在于,如果您在compositeLens
块中给do
命名,那么它必须具有不能再从其上下文中推断出的类型。但是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