Эмпирическое правило, которое упрощает многие вещи, состоит в том, чтобы не принимать 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
Ваша версия 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'
в качестве аргумента, локальное связывание автоматически принимает мономорфный тип: как
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