Я не бачу жодної опублікованої версії синтаксичної форми, підпис якої sugarSym
використовує ці точні імена типів, тому я буду використовувати гілку розробки в команді 8cfd02 ^ , остання версія якої все ще використовувала ці імена.
Отже, чому GHC скаржиться на fi
підпис у вашому типі, а не на той sugarSym
? Документація, з якою ви пов’язані, пояснює, що тип є неоднозначним, якщо він не відображається праворуч від обмеження, якщо тільки обмеження не використовує функціональні залежності, щоб вивести інакше неоднозначний тип з інших неоднозначних типів. Тож давайте порівняємо контексти двох функцій та шукатимемо функціональні залежності.
class ApplySym sig f sym | sig sym -> f, f -> sig sym
class SyntacticN f internal | f -> internal
sugarSym :: ( sub :<: AST sup
, ApplySym sig fi sup
, SyntacticN f fi
)
=> sub sig -> f
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
Отже sugarSym
, для неоднозначних типів є sub
, sig
і f
з них ми повинні мати можливість випливати з функціональних залежностей, щоб розмежувати всі інші типи, що використовуються в контексті, а саме sup
і fi
. Дійсно, f -> internal
функціональна залежність SyntacticN
використовує наше f
для відключення нашої fi
, а потім f -> sig sym
функціональна залежність у ApplySym
використанні нашої недавно розмежованої fi
для роз'єднання sup
(і sig
, що вже було неоднозначним). Отже, це пояснює, чому sugarSym
не потрібно AllowAmbiguousTypes
розширення.
Давайте тепер розглянемо sugar
. Перше, що я помічаю, - це те, що компілятор не скаржиться на неоднозначний тип, а скоріше на екземпляри, що перекриваються:
Overlapping instances for SyntacticN b fi
arising from the ambiguity check for ‘share’
Matching givens (or their superclasses):
(SyntacticN (a -> (a -> b) -> b) fi1)
Matching instances:
instance [overlap ok] (Syntactic f, Domain f ~ sym,
fi ~ AST sym (Full (Internal f))) =>
SyntacticN f fi
-- Defined in ‘Data.Syntactic.Sugar’
instance [overlap ok] (Syntactic a, Domain a ~ sym,
ia ~ Internal a, SyntacticN f fi) =>
SyntacticN (a -> f) (AST sym (Full ia) -> fi)
-- Defined in ‘Data.Syntactic.Sugar’
(The choice depends on the instantiation of ‘b, fi’)
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
Тож якщо я читаю це право, то не те, що GHC вважає, що ваші типи неоднозначні, а скоріше, що під час перевірки, чи ваші типи неоднозначні, GHC зіткнулася з іншою, окремою проблемою. Тоді це говорить вам, що якби ви сказали GHC не проводити перевірку неоднозначності, вона не зіткнулася б з цією окремою проблемою. Це пояснює, чому включення AllowAmbiguousTypes дозволяє компілювати ваш код.
Однак проблема з випадками, що перекриваються, залишається. Два екземпляри, перелічені GHC ( SyntacticN f fi
і SyntacticN (a -> f) ...
), перетинаються один з одним. Як не дивно, схоже, що перший з них повинен перегукуватися з будь-яким іншим екземпляром, який є підозрілим. А що [overlap ok]
означає?
Я підозрюю, що Syntactic складено з OverlappingInsances. А дивлячись на код , справді це так і є.
Експериментуючи трохи, здається, що GHC добре з перекриваючими випадками, коли зрозуміло, що одне суворо загальне, ніж інше:
{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}
class Foo a where
whichOne :: a -> String
instance Foo a where
whichOne _ = "a"
instance Foo [a] where
whichOne _ = "[a]"
-- |
-- >>> main
-- [a]
main :: IO ()
main = putStrLn $ whichOne (undefined :: [Int])
Але GHC не в порядку з випадками, що перекриваються, коли жоден з них, очевидно, не є кращим, ніж інші:
{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}
class Foo a where
whichOne :: a -> String
instance Foo (f Int) where -- this is the line which changed
whichOne _ = "f Int"
instance Foo [a] where
whichOne _ = "[a]"
-- |
-- >>> main
-- Error: Overlapping instances for Foo [Int]
main :: IO ()
main = putStrLn $ whichOne (undefined :: [Int])
Ваш підпис типу використовує 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 більше не скаржиться на перекриття.
Якби я був ти, я би роздивився визначення цих двох можливих екземплярів і визначив, чи є одна з цих двох реалізацій потрібною.
sugarSym Let
, який є(SyntacticN f (ASTF sup a -> ASTF sup (a -> b) -> ASTF sup b), Let :<: sup) => f
і не включає неоднозначні змінні типу?