代码之家  ›  专栏  ›  技术社区  ›  GabiMe

哈斯克尔的“提升”是什么?

  •  121
  • GabiMe  · 技术社区  · 15 年前

    我不明白“提升”是什么。在理解“电梯”是什么之前,我应该先理解单子吗?(我也完全不知道蒙娜斯的名字):或者有人能用简单的词来向我解释一下吗?

    5 回复  |  直到 7 年前
        1
  •  161
  •   Paul Johnson    8 年前

    提升更多的是一种设计模式,而不是一个数学概念(尽管我希望这里的人会通过展示提升是一个类别或什么来反驳我)。

    通常,您有一些带有参数的数据类型。类似的东西

    data Foo a = Foo { ...stuff here ...}
    

    假设你发现 Foo 采用数字类型( Int , Double 等等),你必须编写代码来解开这些数字,加上或乘上它们,然后再把它们包装起来。您可以通过编写一次展开和包装代码来短路这一点。此函数传统上称为“提升”,因为它看起来如下:

    liftFoo2 :: (a -> b -> c) -> Foo a -> Foo b -> Foo c
    

    换句话说,您有一个接受双参数函数的函数(例如 (+) operator)并将其转换为foos的等效函数。

    现在你可以写了

    addFoo = liftFoo2 (+)
    

    编辑:更多信息

    你当然可以 liftFoo3 ,请 liftFoo4 等等。然而,这通常是不必要的。

    从观察开始

    liftFoo1 :: (a -> b) -> Foo a -> Foo b
    

    但这和 fmap . 因此而不是 liftFoo1 你会写信的

    instance Functor Foo where
       fmap foo = ...
    

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

    liftFoo1 = fmap
    

    如果你能做到 变成一个函数,也许你可以使它成为一个应用函数。事实上,如果你能写 liftFoo2 然后应用实例如下:

    import Control.Applicative
    
    instance Applicative Foo where
       pure x = Foo $ ...   -- Wrap 'x' inside a Foo.
       (<*>) = liftFoo2 ($)
    

    这个 (<*>) foo的运算符具有类型

    (<*>) :: Foo (a -> b) -> Foo a -> Foo b
    

    它将wrapped函数应用于wrapped值。所以如果你能实现 升压氧 然后你可以用它来写这个。或者您可以直接实现它,而不必费心 升压氧 ,因为 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包装的新函数。这反过来可以应用于下一个参数,使用 (& lt;*& gt;) 等等。因此,现在,您不必为每个arity都具有提升功能,而只需要一个菊花链应用程序。

        2
  •  36
  •   Martijn    12 年前

    保罗的和亚尔丘的都是很好的解释。

    我想补充一点,被提升的函数可以有任意数量的参数,而且它们不必是相同的类型。例如,还可以定义LiftFoo1:

    liftFoo1 :: (a -> b) -> Foo a -> Foo b
    

    一般来说,获取1个参数的函数的提升是在类型类中捕获的。 Functor 起重作业称为 fmap :

    fmap :: Functor f => (a -> b) -> f a -> f b
    

    注意与 liftFoo1 的类型。事实上,如果你有 升降机1 ,你可以 Foo 一个实例 函子 :

    instance Functor Foo where
      fmap = liftFoo1
    

    此外,提升到任意数量的参数的泛化被称为 应用风格 . 在掌握带固定参数的函数提升之前,不要费心钻研这个问题。但当你这样做的时候, Learn you a Haskell 在这方面有很好的篇章。这个 Typeclassopedia 是另一个描述 函子 应用的 (以及其他类型类;向下滚动到该文档的右侧章节)。

    希望这有帮助!

        3
  •  22
  •   yairchu    15 年前

    让我们从一个例子开始:

    > 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 . 它将一个单胞菌的一个单胞菌动作转换成一个已转换的单胞菌动作。

    通常,将“提升”功能/动作提升为“包裹”类型。

    理解这一点、单子等等以及理解它们为什么有用的最好方法可能是编码和使用它。如果您以前编写的代码中有任何您怀疑可以从中受益的地方(例如,这将使代码变短等),只需尝试一下,您就可以很容易地理解这个概念。

        4
  •  11
  •   Nasser Hadjloo    15 年前

    提升是一个概念,它允许您在另一个(通常更通用)设置中将一个函数转换为相应的函数。

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

        5
  •  0
  •   Val    12 年前

    根据 this shiny tutorial 函数是一个容器(例如 Maybe<a> , List<a> Tree<a> 可以存储其他类型的元素, a )我使用了Java泛型表示法, <a> ,对于元素类型 把这些元素想象成树上的浆果 树& a; . 有一个函数 fmap ,采用元素转换函数, a->b 容器 functor<a> . 适用于 A & GT;B 有效地将容器的每个元素转换为 functor<b> . 当只提供第一个参数时, A & GT;B , FMAP 等待 功能器<A> . 也就是说,供应 A & GT;B 单独将此元素级函数转换为函数 functor<a> -> functor<b> 在容器上操作的。这叫做 举起 功能。因为容器也被称为 函子 ,函数而不是单子是提升的先决条件。单子有点类似于提升。两者都依赖于函子概念和do f<a> -> f<b> . 区别在于起重使用 A & GT;B 对于转换,而monad要求用户定义 a -> f<b> .