Недавно я опубликовал вопрос о синтаксической версии 2.0 относительно определения share
. У меня было это работает в GHC 7.6 :
{-# LANGUAGE GADTs, TypeOperators, FlexibleContexts #-}
import Data.Syntactic
import Data.Syntactic.Sugar.BindingT
data Let a where
Let :: Let (a :-> (a -> b) :-> Full b)
share :: (Let :<: sup,
sup ~ Domain b, sup ~ Domain a,
Syntactic a, Syntactic b,
Syntactic (a -> b),
SyntacticN (a -> (a -> b) -> b)
fi)
=> a -> (a -> b) -> b
share = sugarSym Let
Однако GHC 7.8 хочет -XAllowAmbiguousTypes
скомпилировать эту подпись. С другой стороны , я могу заменить fi
с
(ASTF sup (Internal a) -> AST sup ((Internal a) :-> Full (Internal b)) -> ASTF sup (Internal b))
это тип, на который ссылается fundep SyntacticN
. Это позволяет мне избежать расширения. Конечно это
- очень длинный тип для добавления к уже большой подписи
- утомительно выводить вручную
- ненужный из-за fundep
Мои вопросы:
- Это приемлемое использование
-XAllowAmbiguousTypes
? - В общем, когда следует использовать это расширение? Ответ здесь предполагает, что «это почти никогда не хорошая идея».
Несмотря на то, что я прочитал документы , мне все еще трудно решить, является ли ограничение двусмысленным или нет. В частности, рассмотрим эту функцию из Data.Syntactic.Sugar:
sugarSym :: (sub :<: AST sup, ApplySym sig fi sup, SyntacticN f fi) => sub sig -> f sugarSym = sugarN . appSym
Мне кажется, что
fi
(и, возможно,sup
) здесь должно быть неоднозначным, но он компилируется без расширения. ПочемуsugarSym
однозначно покаshare
есть? Посколькуshare
это приложениеsugarSym
,share
все ограничения исходят прямо изsugarSym
.
sugarSym Let
, который включает(SyntacticN f (ASTF sup a -> ASTF sup (a -> b) -> ASTF sup b), Let :<: sup) => f
и не включает в себя переменные неоднозначного типа?share
, но делает компиляцию , когда - либо из подписей , указанных в вопросе используется.f
само по себе достаточно , чтобы полностью устранить неоднозначностьsig
,fi
иsup
.SyntacticN
делаетfi
однозначным вsugarSym
, но тогда почему то же самое не относится кfi
вshare
?Ответы:
Я не вижу ни одной опубликованной версии синтаксиса, подпись которой
sugarSym
использует эти точные имена типов, поэтому я буду использовать ветку разработки на коммите 8cfd02 ^ , последней версии, которая все еще использовала эти имена.Итак, почему GHC жалуется на
fi
подпись в вашем типе, а не на подписьsugarSym
? В документации, на которую вы ссылаетесь, объясняется, что тип является неоднозначным, если он не отображается справа от ограничения, если только ограничение не использует функциональные зависимости, чтобы вывести неоднозначный в противном случае тип из других не неоднозначных типов. Итак, давайте сравним контексты двух функций и поищем функциональные зависимости.Так
sugarSym
, не-неоднозначные типыsub
,sig
иf
, и от тех , что мы должны быть в состоянии следовать функциональным зависимостям, чтобы неоднозначность всех других типов , используемых в контексте, а именноsup
иfi
. И действительно,f -> internal
функциональная зависимость вSyntacticN
использует нашу,f
чтобы устранить неоднозначностьfi
, и после этогоf -> sig sym
функциональная зависимостьApplySym
использует нашу недавно устраненную неоднозначностьfi
для устранения неоднозначностиsup
(иsig
, которая уже была неоднозначной). Это объясняет, почемуsugarSym
не требуетсяAllowAmbiguousTypes
расширение.Давайте теперь посмотрим
sugar
. Первое, что я заметил, это то, что компилятор не жалуется на неоднозначный тип, а на перекрывающиеся экземпляры:Поэтому, если я правильно понял, это не значит, что GHC думает, что ваши типы неоднозначны, а скорее, что при проверке, являются ли ваши типы неоднозначными, GHC столкнулся с другой, отдельной проблемой. Затем он говорит вам, что если бы вы сказали GHC не выполнять проверку неоднозначности, он бы не столкнулся с этой отдельной проблемой. Это объясняет, почему включение AllowAmbiguousTypes позволяет вашему коду компилироваться.
Однако проблема с перекрывающимися экземплярами остается. Два экземпляра, перечисленные GHC (
SyntacticN f fi
иSyntacticN (a -> f) ...
), перекрывают друг друга. Как ни странно, первый из них должен совпадать с любым другим экземпляром, что является подозрительным. И что это[overlap ok]
значит?Я подозреваю, что синтаксический компилируется с OverlappingInstances. И, глядя на код , это действительно так.
Немного поэкспериментировав, кажется, что GHC в порядке с перекрывающимися экземплярами, когда становится ясно, что одно строго более общее, чем другое:
Но GHC не подходит для перекрывающихся случаев, когда ни один из них явно не подходит лучше, чем другой:
Ваша подпись типа использует
SyntacticN (a -> (a -> b) -> b) fi
, и ни то,SyntacticN f fi
ни другое неSyntacticN (a -> f) (AST sym (Full ia) -> fi)
подходит лучше, чем другие. Если я изменю эту часть подписи вашего типа наSyntacticN a fi
илиSyntacticN (a -> (a -> b) -> b) (AST sym (Full ia) -> fi)
, GHC больше не будет жаловаться на совпадение.На вашем месте я бы посмотрел на определение этих двух возможных экземпляров и определил, является ли одна из этих двух реализаций той, которую вы хотите.
источник
Я обнаружил, что
AllowAmbiguousTypes
это очень удобно для использования сTypeApplications
. Рассмотрим функциюnatVal :: forall n proxy . KnownNat n => proxy n -> Integer
из GHC.TypeLits .Чтобы использовать эту функцию, я мог бы написать
natVal (Proxy::Proxy5)
. Альтернативный стиль для использованияTypeApplications
:natVal @5 Proxy
. ТипProxy
выводится приложением типа, и раздражает необходимость писать его каждый раз, когда вы вызываетеnatVal
. Таким образом, мы можем включитьAmbiguousTypes
и написать:Тем не менее, обратите внимание, что если вы двусмысленно, вы не можете вернуться !
источник