是不是可以让复合镜片束缚?

Pti*_*val 3 haskell unification functor haskell-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
Run Code Online (Sandbox Code Playgroud)

错误消息是:

• 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
Run Code Online (Sandbox Code Playgroud)

和:

• 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
Run Code Online (Sandbox Code Playgroud)

我已经尝试添加类型签名,明确量化fFunctor约束,但到目前为止没有成功.

lef*_*out 6

它简化了很多事情的经验法则是不采取Lens(或Lens',或Setter等)作为函数的参数,除非你真的需要它的光学多态,而是采取ALens(或ALens'ASetter)版本,从而避免了排名-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
Run Code Online (Sandbox Code Playgroud)

您的版本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
Run Code Online (Sandbox Code Playgroud)

... compositeLens再次尝试将刚才给定的类型专门化为一个特定的仿函数,这是无法完成的.

更直接的解决方案是完全避免这种局部多态,你实际上并不需要:如果你把一个ALens'作为参数,本地绑定会自动采用单态类型:like

worksEasily :: ( Member (Reader C) e ) => ALens' C B -> Eff e A
worksEasily bLens = do
  let compositeLens = bLens . a
  askLensed compositeLens
Run Code Online (Sandbox Code Playgroud)

其实并不是这样; 事实证明你不想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
Run Code Online (Sandbox Code Playgroud)