5 ответов:
лифтинг-это скорее шаблон дизайна, чем математическая концепция (хотя я ожидаю, что кто-то здесь теперь опровергнет меня, показав, как лифты являются категорией или чем-то еще).
как правило, у вас есть некоторый тип данных параметра. Что-то вроде
data Foo a = Foo { ...stuff here ...}Предположим, вы обнаружите, что много применений
Fooпринимать числовые типы (Int,Doubleи т. д.), И вы продолжаете писать код, который разворачивает эти числа, добавляет или умножает их, а затем обертывает они отступают. Вы можете закоротить это, написав код unwrap-and-wrap один раз. Эта функция традиционно называется "лифт", потому что она выглядит так:liftFoo2 :: (a -> b -> c) -> Foo a -> Foo b -> Foo cдругими словами, у вас есть функция, которая принимает два аргумента функции (например,
(+)оператор) и превращает его в эквивалентную функцию для Foos.так что теперь вы можете писать
addFoo = liftFoo2 (+)Edit: дополнительная информация
вы может, конечно, есть
liftFoo3,liftFoo4и так далее. Однако часто в этом нет необходимости.начните с наблюдения
liftFoo1 :: (a -> b) -> Foo a -> Foo bно это точно так же, как
fmap. Так что вместоliftFoo1вы пишитеinstance Functor Foo where fmap foo = ...если вы действительно хотите полной регулярности вы можете сказать
liftFoo1 = fmapесли вы можете сделать
Fooв функтор, возможно, вы можете сделать это аппликативный функтор. На самом деле, если вы можете написатьliftFoo2затем аппликативный экземпляр выглядит так:import Control.Applicative instance Applicative Foo where pure x = Foo $ ... -- Wrap 'x' inside a Foo. (<*>) = liftFoo2 ($)The
(<*>)оператор для Foo имеет тип(<*>) :: Foo (a -> b) -> Foo a -> Foo bон применяет обернутую функцию к обернутому значению. Так что если вы можете реализовать
liftFoo2затем вы можете написать это с точки зрения его. Или вы можете реализовать его напрямую и не заморачиваться сliftFoo2, потому чтоControl.Applicativeмодуль включает в себяliftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f cи точно так же есть
liftAиliftA3. Но вы на самом деле не использовать их очень часто, потому что есть еще один оператор(<$>) = fmapэто позволит вам написать:
result = myFunction <$> arg1 <*> arg2 <*> arg3 <*> arg4термин
myFunction <$> arg1возвращает новую функцию, завернутую в Foo. Это, в свою очередь, может быть применено к следующему аргументу с помощью(<*>)и так далее. Так что теперь вместо того, чтобы иметь функцию подъема для каждого arity, у вас просто есть цепочка приложений.
Павел и яирчу оба хорошие объяснения.
Я хотел бы добавить, что поднимаемая функция может иметь произвольное количество аргументов и что они не должны быть одного типа. Например, вы также можете определить liftFoo1:
liftFoo1 :: (a -> b) -> Foo a -> Foo bв общем случае, снятие функций, которые принимают 1 аргумент, захватывается в классе типа
Functor, и подъемная операция называетсяfmap:fmap :: Functor f => (a -> b) -> f a -> f bобратите внимание на сходство с
liftFoo1' s тип. На самом деле, если у вас естьliftFoo1можно сделатьFooэкземплярFunctor:instance Functor Foo where fmap = liftFoo1кроме того, обобщение подъема на произвольное число аргументов называется аппликативный стиль. Не утруждайте себя погружением в это, пока вы не поймете подъем функций с фиксированным количеством аргументов. Но когда вы это сделаете,узнать вы на Haskell хорошая глава об этом. Элемент Typeclassopedia еще один хороший документ, который описывает функтор и аппликативный (а также другие классы типов; прокрутите вниз до правой главы в этом документе).
надеюсь, что это помогает!
давайте начнем с примера:
> replicate 3 'a' "aaa" > :t replicate replicate :: Int -> a -> [a] > :t liftA2 replicate liftA2 replicate :: (Applicative f) => f Int -> f a -> f [a] > (liftA2 replicate) [1,2,3] ['a','b','c'] ["a","b","c","aa","bb","cc","aaa","bbb","ccc"] > :t liftA2 liftA2 :: (Applicative f) => (a -> b -> c) -> (f a -> f b -> f c)
liftA2преобразует функцию простых типов в функцию этих типов, завернутую вApplicative, например списки,IOи т. д.еще один общий лифт составляет
liftСControl.Monad.Trans. Она превращает монадическое действие одной монады в действие трансформированной монады.вообще, подъемы " поднимают "функцию/действие в" обернутый " тип.
лучший способ понять это, и монады и т. д. и поймите, почему они полезны, вероятно, чтобы кодировать и использовать его. Если есть что-то, что вы закодировали ранее, что вы подозреваете, может извлечь выгоду из этого (т. е. это сделает этот код короче и т. д.), Просто попробуйте, и вы легко поймете концепцию.
лифтинг-это концепция, которая позволяет преобразовать функцию в соответствующую функцию в рамках другой (обычно более общей) настройки
взгляните на http://haskell.org/haskellwiki/Lifting
по данным этот блестящий учебник, функтор-это некоторый контейнер (например,
Maybe<a>,List<a>илиTree<a>который может хранить элементы какого-то другого типа,a). Я использовал нотацию Java generics,<a>, для элементов типаaи думать о элементах, как ягоды на деревеTree<a>. Есть функцияfmap, которая принимает функцию преобразования элементаa->bи контейнерfunctor<a>. Это относитсяa->bк каждому элементу контейнера эффективно преобразование его вfunctor<b>. Если указан только первый аргумент,a->b,fmapждетfunctor<a>. То есть, поставляяa->bв одиночку превращает эту функцию уровня элемента в функциюfunctor<a> -> functor<b>это работает над контейнерами. Это называется подъем функции. Потому что контейнер также называется функтор, функторы, а не монады являются необходимым условием для подъема. Монады как бы "параллельны" подъему. Оба полагаются на функтор понятие и делатьf<a> -> f<b>. Разница в том, что лифтинг используетa->bдля преобразования в то время как монада требует от пользователя, чтобы определитьa -> f<b>.
Comments