Вот мой минимальный пример:Как я могу устранить двусмысленность этого типа?
{-# LANGUAGE MultiParamTypeClasses, RankNTypes #-}
import Control.Lens
class Into outer inner where
factory :: inner -> outer
merge :: inner -> inner -> inner
-- Given an inner item, a lens and an outer item, use factory to construct a new
-- outer around the inner if the Maybe outer is Nothing, or else use merge to combine
-- the argument inner with the one viewed through the lens inside the outer
into :: Into outer inner =>
inner -> Lens' outer inner -> Maybe outer -> Maybe outer
inner `into` lens = Just . maybe (factory inner) (over lens (merge inner))
Это не может скомпилировать со следующей ошибкой:
GHCi, version 7.6.2: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
[1 of 1] Compiling Main (foo.hs, interpreted)
foo.hs:10:62:
Could not deduce (Into outer0 inner) arising from a use of `merge'
from the context (Into outer inner)
bound by the type signature for
into :: Into outer inner =>
inner -> Lens' outer inner -> Maybe outer -> Maybe outer
at foo.hs:9:9-84
The type variable `outer0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
In the second argument of `over', namely `(merge inner)'
In the second argument of `maybe', namely
`(over lens (merge inner))'
In the second argument of `(.)', namely
`maybe (factory inner) (over lens (merge inner))'
Failed, modules loaded: none.
Prelude>
Я понимаю, почему происходит эта ошибка; что для вызова merge
может использоваться другой экземпляр Into
(с другим outer
, но тот же inner
), что и тот, который был выбран ограничением по всей функции into
. Но я не могу понять, как это решить.
Что я пробовал:
- Использование функциональных зависимостей, подразумевает
outer
отinner
; это приблизилось к работе (жаловался на необходимостьUndecidableInstances
), но не кажется совершенно правильным; в идеале я бы действительно хотел иметь способ нажимать то же самоеinner
на два разныхouter
s - Использование ассоциированного синонима типа для того же; кроме углекинга подписи типа (
outer
=>Outer inner
), я также упал, потому чтоouter
, который я использую в экземпляре, имеет больше переменных типа (один из них фантом), чемinner
, то есть я не смог законно создать экземпляр связанный тип в заявлении экземпляра - Добавление явной подписи типа при использовании
merge
вinto
сScopedTypeVariables
для привязки его к типу подписи дляinto
; но так как типmerge
не относится кouter
это не помогает
Есть ли способ, что я могу быть явно об использовании того же класса экземпляра типа для merge
как для всей into
? Или каким-либо другим способом я могу ограничить систему типов, требующую этого? В идеале я хотел бы держать класс так, чтобы мои объявления экземпляров все еще это просто:
instance (Hashable v, Eq v) => Into (VarInfo s k v) (HashSet v) where
-- VarInfo is just a record type with 2 fields, the second being a HashSet v
factory = VarInfo (return())
merge = HashSet.intersection
Примечание: 'Lens'' является полиморфным, поэтому функция, которая принимает его как аргумент, получает тип ранга-2. Если вы просто используете 'over', вы можете дать ему более простой тип ранга 1 (который также будет более полиморфным, поскольку он будет работать для' Setter' и т. Д.). (Даже если вы используете каждую операцию с объективом, вы можете принять 'ALens'', а затем использовать' cloneLens' или что-то в этом роде.) – shachaf
@shachaf Спасибо; Я все еще обволакиваю все содержимое пакета объективов.Я знал, что «Ленс» был менее общим, чем нужно, но у меня есть линзы, и я знаю, что означает «Ленс», тогда как предполагаемый тип для этого параметра был непонятным тарабарщиной для меня на этом этапе (не уверен, что это был бы дружественный синоним, если бы он был). В чем преимущество типов ранга 1? – Ben
Главным преимуществом является то, что вывод типа для классов ранга 2 не очень хорошо работает в GHC. В конечном итоге вы нуждаетесь в eta-expand, напишите явные типы, где GHC, как правило, сможет их вывести и т. Д. Также этот тип распространяется на каждого пользователя вашей функции - вам требуется больше полиморфизма, чем это необходимо. (Возможно, также могут быть последствия для производительности, так как вам нужно передавать явные словари, если все не встанет в очередь ... Я еще не проверил.) – shachaf