2017-02-05 19 views
10

this После ответа, я реализовал обобщенную функцию подъема в моей программе:Понимание forall в Monad '>> =' функция?

liftTupe :: (x -> c x) -> (a, b) -> (c a, c b) --This will error 
liftTuple :: (forall x. x -> c x) -> (a, b) -> (c a, c b) 

Я понимаю, что в этом контексте, forall это позволяет x быть любого типа ([], Maybe и т.д ..) ,

Я теперь, глядя в определение >>= в монад:

class Applicative m => Monad m where 
    (>>=) :: forall a b. m a -> (a -> m b) -> m b 

Я не могу понять роль этого forall в определении функции? Поскольку, в отличие от liftTuple, он не привязан к определенной функции (x -> c x)?

+5

Не знаю, почему все ответы так многословны. В одном предложении: вы правы; он ничего не меняет и просто должен быть явным. – Ryan

+0

'forall a b. <..> 'в типе' >> = '* * привязан к определенной функции - функция' m a -> (a -> m b) -> m b'. Вы также можете иметь 'forall' без функции, например. '[] :: forall a. [a] ',' empty :: forall f a. Альтернатива f => f a'. – user2407038

+1

@ Ryan Когда я получаю ответ: «Потому что все неявно квалифицировано». мой последующий вопрос часто «Почему?». – ThreeFx

ответ

6

В принципе, если вы не используете forall, все типы являются глобальными в определении функции, что означает, что все они выведены при вызове функции. С forall вы можете отказаться от того, что для функции, принимающей x, пока она не назовет себя.

Так в первом у вас есть функция, которая принимает x и дает c x, то у вас есть кортеж с a и b, и вы ожидаете кортеж с c a и c b. Поскольку вы уже сказали, что первая функция принимает x, вы можете сделать x таким же, как a, но это не будет b, потому что x определяется один раз для всей декларации. Таким образом, вы не можете заставить функцию принимать как a, так и b.

Однако во втором случае область действия x ограничена функцией, принимающей x. Мы в основном говорим, что есть функция, которая берет что-то и делает c с чем-то, и это может быть любой тип. Это позволяет нам сначала подать a, а затем b, и он будет работать. x не должно быть чем-то особенным сейчас снаружи.

Что вы видите в определении Monad - это расширение языка «Явное право». Существует описание на Haskell Prime для этого расширения

ExplicitForAll позволяет использовать ключевое слово «FORALL», чтобы явно указать, что тип является полиморфным в своем свободном переменном типе. Он не позволяет записывать какие-либо типы, которые уже не могут быть записаны; он просто позволяет программисту явно указать (в настоящее время неявное) количественное определение.

Это языковое расширение является чисто визуальным, позволяет выписывать переменные явно, которые вы ранее не могли. Вы можете просто опустить forall a b. из объявления Monad, и программа будет функционировать точно так же.

Скажите, с помощью этого расширения вы можете переписать liftTupe как forall a b x. (x -> c x) -> (a, b) -> (c a, c b). Определение одно и то же, и оно функционирует одинаково, но теперь читатели ясно видят, что все переменные типа определены на самом верхнем уровне.

+0

@mschmidt Хороший улов, я разберу ответ. – Malcolm

+0

@ThreeFx Я писал о 'liftTupe', а не' liftTuple'. – Malcolm

+0

Ой, мой плохой, я думал, что это опечатка! – ThreeFx

3

Все переменные типа в системе типа Haskell определяются количественно с помощью forall.Тем не менее, GHC может вывести количественную оценку во многих случаях, поэтому вам не нужно записывать их в исходный код.

Например тип liftTuple с forall явной является

liftTuple :: forall c a b. (forall x. x -> c x) -> (a, b) -> (c a, c b) 

И >>= случае одно и то же.

5

Каждой функции писать неявно квантор всеобщности над ее переменным типом:

id :: a -> a   -- this is actually universally quantified over a 
id :: forall a. a -> a 
id x = x 

Вы действительно можете превратить это поведение с ExplicitForall языком прагмой.

Это свойство очень полезно, так как оно сдерживает вас от написания кода, который работает только с некоторыми типами. Подумайте, что может сделать функция id: она может либо вернуть свой аргумент, либо цикл навсегда. Это единственные две вещи, которые он может сделать, и вы можете понять это на основе его сигнатуры типа.

Обеспечения всех экземпляров полиморфных функций ведут себя точно так же, независимо от типа аргумента называется parametricity и объясняется в this блоге на Бартоше Milewski. TL; DR: Используя параметричность, мы можем гарантировать, что некоторые переупорядочения в структуре программы не влияют на ее поведение. Для математически более строгой обработки этого, см. Theorems for free! Филиппом Вадлером.

2

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

Итак, давайте посмотрим на разницу между двумя использования FORALL и как Haskell могли бы видеть их:

неявной:

foo :: (x -> f x) -> a -> b -> (f a, f b) 
-- same as 
foo :: forall f x a b . (x -> f x) -> a -> b -> (f a, f b) 
-- our function is applied to a, so x is equal to a 
foo :: forall f x a b . (x ~ a) => (x -> f x) -> a -> b -> (f a, f b) 
-- our function is also applied to b, so x is equal to b 
foo :: forall f x a b . (x ~ a, x ~ b) => (x -> f x) -> a -> b -> (f a, f b) 

Ой, (х ~ а, х ~ б) потребует (a ~ b). Это было бы выведено без аннотации, но поскольку мы явно использовали разные переменные типа, все взрывается. Чтобы решить это, нам нужно, чтобы f оставалась полиморфной внутри нашей функции.

Стандарт haskell не может выразить это, поэтому нам понадобятся rank2types или rankntypes. С этим мы можем написать:

foo :: (forall x . x -> f x) -> a -> b -> (f a, f b) 

Обратите внимание, что forall является частью типа функции. Таким образом, он остается полиморфным в нашей функции, и мы можем применять его к разным типам без всякого взрыва!

Обратите внимание, что мы также просто могли бы сделать:

foo :: Monad m => a -> b -> (m a, m b) 
foo a b = (return a, return b) 
+1

На самом деле вам не нужно «Monad», «pure» из «Applicative» - это достаточно хорошо. И на самом деле «Pointed» еще слабее. – ThreeFx

+0

Хороший вопрос! Является ли аппликация суперкласса монады в языковом стандарте? – Taren

+0

Да, я считаю, что с GHC 7.8. Если вы хотите просмотреть его, это называется предложением AMP или Applicative-Monad. – ThreeFx

 Смежные вопросы

  • Нет связанных вопросов^_^