W bibliotece vinyl znajduje się rodzina typów RecAll
, która pozwala nam zapytać, czy ograniczenie stosowane częściowo jest prawdziwe dla każdego typu na liście poziomu typów. Na przykład, możemy napisać to:Osłabienie wiązania RecAll winylu przez wymuszenie
myShowFunc :: RecAll f rs Show => Rec f rs -> String
To wszystko jest piękne. Teraz, jeśli mamy ograniczenie RecAll f rs c
, gdzie c
jest nieznany i wiemy, że c x
zawiera d x
(aby wypożyczyć język z pakietu ekmett's contstraints), jak możemy uzyskać RecAll f rs d
?
Powodem, dla którego pytam, jest to, że pracuję z rekordami w niektórych funkcjach, które wymagają spełnienia kilku ograniczeń typeclass. Aby to zrobić, używam kombinatora :&:
z modułu Control.Constraints.Combine w pakiecie exists. (Uwaga: pakiet nie zostanie skompilowany, jeśli masz zainstalowane inne rzeczy, ponieważ zależy to od bardzo starej wersji contravariant
. Możesz po prostu skopiować jeden moduł, o którym wspomniałem.) Dzięki temu mogę uzyskać naprawdę piękne ograniczenia, podczas gdy zminimalizowanie brojlerki typeclass. Na przykład:
RecAll f rs (TypeableKey :&: FromJSON :&: TypeableVal) => Rec f rs -> Value
Ale w ciele tej funkcji wzywam inną funkcję, która wymaga słabszego ograniczenia. Może to wyglądać następująco:
RecAll f rs (TypeableKey :&: TypeableVal) => Rec f rs -> Value
GHC nie widzi, że drugie zdanie wynika z pierwszego. Zakładałem, że tak będzie. Nie mogę zobaczyć, jak to udowodnić, aby to potwierdzić i pomóc GHC. Do tej pory mam to:
import Data.Constraint
weakenAnd1 :: ((a :&: b) c) :- a c
weakenAnd1 = Sub Dict -- not the Dict from vinyl. ekmett's Dict.
weakenAnd2 :: ((a :&: b) c) :- b c
weakenAnd2 = Sub Dict
Te działają dobrze. Ale tutaj utknąłem:
-- The two Proxy args are to stop GHC from complaining about AmbiguousTypes
weakenRecAll :: Proxy f -> Proxy rs -> (a c :- b c) -> (RecAll f rs a :- RecAll f rs b)
weakenRecAll _ _ (Sub Dict) = Sub Dict
To nie kompiluje. Czy ktoś jest świadomy sposobu uzyskania efektu, którego szukam? Tutaj są błędy, jeśli są pomocne. Również mam Dict
jako wykwalifikowany importu w moim rzeczywisty kod, więc dlatego wspomina Constraint.Dict
:
Table.hs:76:23:
Could not deduce (a c) arising from a pattern
Relevant bindings include
weakenRecAll :: Proxy f
-> Proxy rs -> (a c :- b c) -> RecAll f rs a :- RecAll f rs b
(bound at Table.hs:76:1)
In the pattern: Constraint.Dict
In the pattern: Sub Constraint.Dict
In an equation for ‘weakenRecAll’:
weakenRecAll _ _ (Sub Constraint.Dict) = Sub Constraint.Dict
Table.hs:76:46:
Could not deduce (RecAll f rs b)
arising from a use of ‘Constraint.Dict’
from the context (b c)
bound by a pattern with constructor
Constraint.Dict :: forall (a :: Constraint).
(a) =>
Constraint.Dict a,
in an equation for ‘weakenRecAll’
at Table.hs:76:23-37
or from (RecAll f rs a)
bound by a type expected by the context:
(RecAll f rs a) => Constraint.Dict (RecAll f rs b)
at Table.hs:76:42-60
Relevant bindings include
weakenRecAll :: Proxy f
-> Proxy rs -> (a c :- b c) -> RecAll f rs a :- RecAll f rs b
(bound at Table.hs:76:1)
In the first argument of ‘Sub’, namely ‘Constraint.Dict’
In the expression: Sub Constraint.Dict
In an equation for ‘weakenRecAll’:
weakenRecAll _ _ (Sub Constraint.Dict) = Sub Constraint.Dict
Myślę, że przynajmniej część problemu polega na tym, że "c" jest kwantyfikowane w niewłaściwym miejscu ... –