Какое расширение FlexibleContexts полезно? Не могли бы вы объяснить это простым примером?
Я пытался понять, что делает расширение FlexibleContexts, ища веб-страницы, которые объяснят это простым смертным (люди, которые читали LYHFGG, например, как я), но я не нашел такого ресурса.
Поэтому Я спрашиваю экспертов по теме: Может кто-нибудь объяснить, что это расширение делает, почему оно существует, и дать один или два простых примера, как и почему его следует использовать?
Кроме того. Если я читаю код другого пользователя который использует это расширение, то что я должен знать о чтобы понять код, написанный с использованием этого расширения?
Ответы
Ответ 1
Без FlexibleContexts
все ограничения класса типов в определениях функций должны иметь переменные типа. Например:
add :: Num a => a -> a -> a
add = (+)
Где a
- переменная типа. С включенным FlexibleContexts
вы можете иметь любой тип внутри класса типов.
intAdd :: Num Int => Int -> Int -> Int
intAdd = (+)
Этот пример довольно надуманный, но он самый простой, о котором я могу подумать. FlexibleContexts
обычно используется только с MultiParamTypeClasses
. Вот пример:
class Shower a b where
myShow :: a -> b
doSomething :: Shower a String => a -> String
doSomething = myShow
Здесь вы можете видеть, что мы говорим, что мы хотим только Shower a String
. Без FlexibleContexts
String
должен был бы быть переменной типа, а не конкретным типом.
Ответ 2
Обычно он используется с расширением MultiParamTypeClasses
, например, при использовании библиотеки mtl
, которую вы могли бы написать
doSomethingWithState :: MonadState MyState m => m ()
doSomethingWithState = do
current <- get
let something1 = computeSomething1 current
something2 = computeSomething2 current something1
put something2
И аналогично с MonadReader
и MonadWriter
, наряду с другими аналогичными типами. Без FlexibleContexts
вы не можете использовать это ограничение.
(Обратите внимание, что этот ответ был основан на @DiegoNolan's, но переписан, чтобы использовать существующую библиотеку, которая должна иметь смысл для читателей LYAH).
Ответ 3
Я обнаружил использование для этого помимо упомянутых: это приводит к более ясным сообщениям об ошибках от GHC. Например. как правило,
Prelude> max (1, 2) 3
<interactive>:1:1: error:
• Non type-variable argument in the constraint: Num (a, b)
(Use FlexibleContexts to permit this)
• When checking the inferred type
it :: forall a b.
(Num (a, b), Num b, Num a, Ord b, Ord a) =>
(a, b)
И с включенными FlexibleContexts:
Prelude> max (1, 2) 3
<interactive>:1:1: error:
• No instance for (Num (Integer, Integer))
arising from a use of ‘it
• In the first argument of ‘print, namely ‘it
In a stmt of an interactive GHCi command: print it
Здесь обсуждение.
Ответ 4
FlexibleContexts
часто используется с семействами типов. Например, при использовании GHC.Generics
часто встречаются подписи типа
foo :: (Generic a, GFoo (Rep a)) => Int -> a -> a
Это можно рассматривать как вариант использования MultiParamTypeClasses
:
class (Generic a, rep ~ Rep a) => MPGeneric rep a
instance (Generic a, rep ~ Rep a) => MPGeneric rep a
mpFoo :: (MPGeneric rep a, GFoo rep) => Int -> a -> a
Как указал AJFarmar, FlexibleContexts
также полезен не для MPTC и семейств типов. Вот простой пример:
newtype Ap f a = Ap (f a)
deriving instance Show (f a) => Show (Ap f a)
Альтернативный подход с использованием Show1
значительно более неудобен.
Более сложный пример предоставлен комментарием AJFarmar:
data Free f a = Pure a | Free (f (Free f a))
deriving instance (Show a, Show (f (Free f a))) => Show (Free f a)
Это также приводит к UndecidableInstances
, поскольку он рекурсивный, но он хорошо объясняет, что ему нужно показать Free f a
. В ультрасовременном GHC Haskell альтернативой будет использование QuantifiedConstraints
:
deriving instance (Show a, forall x. Show x => Show (f x)) => Show (Free f a)
но это излишне, потому что нам нужно только показать f
, примененный к Free f a
.