haskell - 复合镜头不能装订吗?
问题描述
通过帮助类型检查器,我无法理解以下是否可行,或者完全不可能。设置有点随意,我只需要一些带有镜头的嵌套数据类型,这里称为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
andFunctor
约束,但到目前为止没有成功。
解决方案
简化许多事情的经验法则是,除非您真的需要光学多态性,否则不要将Lens
(or Lens'
, orSetter
等) 作为函数参数,而是采用ALens
(or ALens'
or 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
... wherecompositeLens
再次尝试将刚刚给定的类型专门化为一个特定的函子,这是无法做到的。
更直接的解决方案是完全避免这种你实际上并不需要的局部多态性:如果你将 anALens'
作为参数,则局部绑定会自动采用单态类型:比如
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
推荐阅读
- sql - Spark SQL 中等效的 Teradata TRANSLATE_CHK 函数
- javascript - 在 d3.json() 中创建函数和数组
- python - Pygame tilemap 碰撞(使其返回正在碰撞的一侧)
- java - 带有 Spring Boot Server Notification() 构造函数的 FCM 消息传递问题
- php - 仅在电子邮件附件中获取一个文件而不是多个附件
- android - 如果以编程方式添加 ArFragment 会隐藏一些关于 Acitivity 的视图
- dependency-injection - Autofac 如何替换已注册的服务?
- c - 我如何 PIPE 文件输入或输出缓冲区刷新?
- doxygen - Doxygen Seperate Inc/ 和 Src/ 文件问题
- java - Java - JAXB 将元素值读取为数组而不存储其字符串值