Подтвердить что ты не робот

Какое расширение FlexibleContexts полезно? Не могли бы вы объяснить это простым примером?

Я пытался понять, что делает расширение FlexibleContexts, ища веб-страницы, которые объяснят это простым смертным (люди, которые читали LYHFGG, например, как я), но я не нашел такого ресурса.

Поэтому Я спрашиваю экспертов по теме: Может кто-нибудь объяснить, что это расширение делает, почему оно существует, и дать один или два простых примера, как и почему его следует использовать?

Кроме того. Если я читаю код другого пользователя который использует это расширение, то что я должен знать о чтобы понять код, написанный с использованием этого расширения?

4b9b3361

Ответ 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.