Haskell:什么是“解除”?

我不明白“解除”是什么。 在理解什么是“电梯”之前,我应该先了解Monad(我对Monad还完全无知:)。 或者有人可以用简单的话向我解释?

举重更像是一种devise模式,而不是一个math概念(尽pipe我希望这里的某个人能够通过展示升降机是一个类别还是某个东西来反驳我)。

通常你有一些参数的数据types。 就像是

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

假设你发现Foo的很多用途都带有数字types( IntDouble等),并且你不得不编写解开这些数字的代码,将它们相加或者相乘,然后将它们包装起来。 您可以通过编写一次解包和打包代码来将其短路。 这个函数传统上被称为“提升”,因为它看起来像这样:

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

换句话说,你有一个函数使用两个参数的函数(如(+)运算符),并将其转换为Foos的等价函数。

所以,现在你可以写

 addFoo = liftFoo2 (+) 

编辑:更多信息

你当然可以有liftFoo3liftFoo4等等。 但是这通常不是必须的。

从观察开始

 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 ($) 

Foo的(<*>)运算符有这个types

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

它将包装函数应用于包装值。 所以,如果你能实现liftFoo2那么你可以用它来写这个。 或者你可以直接实现它,而不用担心liftFoo2 ,因为Control.Applicative模块包含

 liftA2 :: Applicative f => (a -> b -> c) -> fa -> fb -> fc 

同样有liftAliftA3 。 但是由于还有另一个操作员,所以你不会经常使用它们

 (<$>) = fmap 

这让你写:

 result = myFunction <$> arg1 <*> arg2 <*> arg3 <*> arg4 

术语myFunction <$> arg1返回一个包含在Foo中的新函数。 这又可以使用(<*>)应用于下一个参数,依此类推。 所以现在,不是每个人都有一个升降function,你只需要一个菊花链应用程序。

Paul和Yairchu都是很好的解释。

我想补充说的是,被解除的函数可以有任意数量的参数,而且它们不必是相同的types。 例如,你也可以定义一个liftFoo1:

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

一般来说,取出1个参数的函数在类Functor被捕获,并且提升操作被称为fmap

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

注意与liftFoo1types的相似性。 事实上,如果你有liftFoo1 ,你可以让Foo成为Functor一个实例:

 instance Functor Foo where fmap = liftFoo1 

此外,将任意数量的论点推广到一般化也叫做应用式 。 在你掌握固定数量的参数来解除函数之前,不要费心钻研。 但是,当你这样做, 学习你一个Haskell有一个很好的一章。 Typeclassopedia是另一个很好的文档,它描述了FunctorApplicative (以及其他types的类;向下滚动到该文档中的右侧章节)。

希望这可以帮助!

我们从一个例子开始:

 > replicate 3 'a' "aaa" > :t replicate replicate :: Int -> a -> [a] > :t liftA2 replicate liftA2 replicate :: (Applicative f) => f Int -> fa -> 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) -> (fa -> fb -> fc) 

liftA2将普通types的函数转换为包含在Applicative的这些types的函数,例如列表, IO等。

另一个常见的电梯是从Control.Monad.Trans lift 。 它将一个单子的单子动作变换成一个变形单子的动作。

一般来说,升降机将function/动作“提升”为“包装”types。

理解这个和monad等最好的方法,并理解为什么他们有用,可能是编码和使用它。 如果以前编码的东西是你怀疑可以从中受益的(也就是说这会使代码变短),那么试试看,你会很容易理解这个概念。

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

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

根据这个shiny的教程 ,一个函子是一些容器(如Maybe<a>List<a>Tree<a> ,可以存储另一种types的元素, a )。 我已经使用了Javagenerics符号<a> ,对于元素typesa ,并将元素视为树Tree<a>上的浆果。 有一个函数fmap ,它需要一个元素转换函数, a->b和容器functor<a> 。 它将a->b到容器的每个元素,并有效地将其转换为functor<b> 。 当仅提供第一个参数时, a->bfmap等待functor<a> 。 也就是说,单独提供a->b将这个元素级函数转换成对容器进行操作的函数functor<a> -> functor<b> 。 这被称为解除function。 因为容器也被称为函子 ,所以函子而不是Monad是提升的先决条件。 Monads与提升有些“平行”。 两者都依赖于Functor的概念,并做f<a> -> f<b> 。 不同的是,提升使用a->b进行转换,而Monad则要求用户定义a -> f<b>