什么是Haskell的"提升"?

Gab*_*iMe 129 haskell functional-programming

我不明白"提升"是什么.在了解"电梯"是什么之前我应该​​先了解单子吗?(我对monads也完全无知:)或者有人可以用简单的词语向我解释一下吗?

Pau*_*son 173

提升更多的是一种设计模式,而不是一种数学概念(虽然我希望周围的人现在可以通过展示升降机是一类什么类型来反驳我).

通常,您有一些带参数的数据类型.就像是

data Foo a = Foo { ...stuff here ...}
Run Code Online (Sandbox Code Playgroud)

假设你发现了很多的用途,Foo采取数值类型(Int,Double等等),并保留其编写解开这些数字,添加或将它们相乘,然后包装它们备份代码.您可以通过编写一次unwrap-and-wrap代码来使其短路.此功能传统上称为"提升",因为它看起来像这样:

liftFoo2 :: (a -> b -> c) -> Foo a -> Foo b -> Foo c
Run Code Online (Sandbox Code Playgroud)

换句话说,你有一个带有两个参数函数的函数(比如(+)运算符)并将它转换为Foos的等价函数.

所以现在你可以写

addFoo = liftFoo2 (+)
Run Code Online (Sandbox Code Playgroud)

编辑:更多信息

你当然可以拥有liftFoo3,liftFoo4等等.然而,这通常是不必要的.

从观察开始

liftFoo1 :: (a -> b) -> Foo a -> Foo b
Run Code Online (Sandbox Code Playgroud)

但这完全一样fmap.而不是liftFoo1你会写

instance Functor Foo where
   fmap f foo = ...
Run Code Online (Sandbox Code Playgroud)

如果你真的想要完全规律,你可以说

liftFoo1 = fmap
Run Code Online (Sandbox Code Playgroud)

如果你可以Foo成为一个仿函数,也许你可以把它变成一个应用函子.事实上,如果你可以写,liftFoo2那么applicative实例看起来像这样:

import Control.Applicative

instance Applicative Foo where
   pure x = Foo $ ...   -- Wrap 'x' inside a Foo.
   (<*>) = liftFoo2 ($)
Run Code Online (Sandbox Code Playgroud)

(<*>)Foo 的操作员有类型

(<*>) :: Foo (a -> b) -> Foo a -> Foo b
Run Code Online (Sandbox Code Playgroud)

它将包装函数应用于包装值.因此,如果您可以实施,liftFoo2那么您可以根据它来编写.或者你可以直接实现它而不用费心liftFoo2,因为Control.Applicative模块包括

liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
Run Code Online (Sandbox Code Playgroud)

同样有liftAliftA3.但是你实际上并没有经常使用它们,因为还有另一个操作员

(<$>) = fmap
Run Code Online (Sandbox Code Playgroud)

这可以让你写:

result = myFunction <$> arg1 <*> arg2 <*> arg3 <*> arg4
Run Code Online (Sandbox Code Playgroud)

该术语myFunction <$> arg1返回包含在Foo中的新函数.这反过来可以应用于下一个参数(<*>),依此类推.因此,现在不是为每个arity提供升力功能,而是只有一个菊花链应用程序.

  • 值得提醒的是,升降机应该遵守标准法则"升力id == id"和"升力(f.g)==(升力f)".(举g)`. (26认同)
  • 电梯确实是"一类或某类".Carlos刚刚列出了Functor定律,其中`id`和`.`分别是某类别的标识箭头和箭头组成.通常在谈到Haskell时,所讨论的类别是"Hask",其箭头是Haskell函数(换句话说,`id`和`.`指的是你熟悉和喜爱的Haskell函数). (12认同)
  • 这应该是`实例Functor Foo`,而不是`实例Foo Functor`,对吧?我会编辑自己,但我不是100%肯定. (3认同)
  • 没有申请人的提升是= Functor.我的意思是你有两个选择:Functor或Applicative Functor.第一个提升单个参数用于第二个多参数功能.差不多就是这样.对?这不是火箭科学:)它只是听起来像它.谢谢你的答案很棒btw! (2认同)
  • @atc:这是部分应用程序。参见https://wiki.haskell.org/Partial_application (2认同)

Mar*_*ijn 40

Paul和yairchu都是很好的解释.

我想补充说,被提升的函数可以有任意数量的参数,并且它们不必是相同的类型.例如,您还可以定义liftFoo1:

liftFoo1 :: (a -> b) -> Foo a -> Foo b
Run Code Online (Sandbox Code Playgroud)

通常,在类型类中捕获取1个参数的函数Functor,并调用提升操作fmap:

fmap :: Functor f => (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

注意与liftFoo1类型的相似性.事实上,如果你有liftFoo1,你可以做Foo一个实例Functor:

instance Functor Foo where
  fmap = liftFoo1
Run Code Online (Sandbox Code Playgroud)

此外,提升到任意数量的参数的概括被称为应用风格.在掌握具有固定数量参数的函数解除之前,不要费心去做.但是当你这样做的时候,了解一下Haskell有一个很好的章节.该Typeclassopedia是描述另一个很好的文件函子应用型(以及其他类型的类;向下滚动到文档中的正确章).

希望这可以帮助!


yai*_*chu 24

让我们从一个例子开始:

> import Control.Applicative
> replicate 3 'a'
"aaa"
> :t replicate
replicate        ::         Int -> b -> [b]
> :t liftA2
liftA2 :: (Applicative f) => (a -> b -> c) -> (f a -> f b -> f c)
> :t liftA2 replicate
liftA2 replicate :: (Applicative f) =>       f Int -> f b -> f [b]
> (liftA2 replicate) [1,2,3] ['a','b','c']
["a","b","c","aa","bb","cc","aaa","bbb","ccc"]
> ['a','b','c']
"abc"
Run Code Online (Sandbox Code Playgroud)

liftA2将普通类型的函数转换为包含在Applicative诸如列表IO等中的这些类型的函数.

另一个常见的电梯lift来自Control.Monad.Trans.它将一个monad的monadic动作转换为变形monad的动作.

通常,升降机将功能/动作"提升"为"包裹"类型.

了解它的最佳方法,monad等以及理解它们有用的原因,可能是编码和使用它.如果您之前编写过任何您怀疑可以从中受益的内容(即这会使代码更短等),只需尝试一下即可轻松掌握这一概念.


Nas*_*loo 14

提升是一个概念,它允许您将功能转换为另一个(通常更一般)设置中的相应功能

看看http://haskell.org/haskellwiki/Lifting

  • 是的,但那页开始"我们通常以(协变)仿函数开始......".不完全是新手友好. (38认同)
  • 这是我在其他函数式编程站点上看到的一个问题; 每个概念都是根据其他(不熟悉的)概念来解释的,直到新手完全循环(并绕过弯道).喜欢递归必须要做些什么. (6认同)
  • 仅当您已经了解该主题时,这样的答案才有用。 (4认同)
  • 但是"functor"是链接的,所以新手可以点击它来查看Functor是什么.不可否认,链接页面并不是那么好.我需要一个帐户并解决这个问题. (3认同)
  • 为这个链接投票。Lift 连接一个世界和另一个世界。 (3认同)