8

目前我正在完成20 个中级 Haskell 练习,这是一个非常有趣的练习。Functor它涉及实现类型类和Monad(以及将Functors 和s 作为参数的函数)的各种实例,Monad但使用可爱的名称,例如FurryandMisty来掩饰我们正在做的事情(产生一些有趣的代码)。

我一直在尝试以无点的方式来做这件事,我想知道是否有一个通用的方案可以将有点(?)定义变成无点定义。例如,这是 的类型类Misty

class Misty m where
  unicorn :: a -> m a
  banana :: (a -> m b) -> m a -> m b

(功能unicornbananareturn>>=,以防不明显)这是我的apple(相当于flip ap)的实现:

apple :: (Misty m) => m a -> m (a -> b) -> m b
apple x f = banana (\g -> banana (unicorn . g) x) f

练习的后面部分让你实现版本liftMliftM2。这是我的解决方案:

appleTurnover :: (Misty m) => m (a -> b) -> m a -> m b
appleTurnover = flip apple

banana1 :: (Misty m) => (a -> b) -> m a -> m b
banana1 =  appleTurnover . unicorn

banana2 :: (Misty m) => (a -> b -> c) -> m a -> m b -> m c
banana2 f = appleTurnover . banana1 f

banana3 :: (Misty m) => (a -> b -> c -> d) -> m a -> m b -> m c -> m d
banana3 f x = appleTurnover . banana2 f x

banana4 :: (Misty m) => (a -> b -> c -> d -> e) -> m a -> m b -> m c -> m d -> m e
banana4 f x y = appleTurnover . banana3 f x y

现在,banana1(相当于liftMfmap)我能够通过合适的定义以无点风格实现appleTurnover. 但是对于其他三个函数,我不得不使用参数。

我的问题是:是否有将此类定义转换为无点定义的方法

4

5 回答 5

11

正如该pointfree实用程序所展示的,可以自动进行任何此类转换。然而,结果往往被混淆而不是改进。如果一个人的目标是增强易读性而不是破坏它,那么第一个目标应该是确定表达式为何具有特定结构,找到合适的抽象,并以这种方式构建事物。

最简单的结构是简单地将事物链接在一个线性管道中,这是简单的函数组合。这让我们靠自己走得很远,但正如你所注意到的,它并不能处理所有事情。

一种概括是具有附加参数的函数,这些参数可以增量构建。这是一个示例:定义onResult = (. (.)). 现在,将onResultn 次应用于初始值id为您提供具有 n 元函数结果的函数组合。所以我们可以定义comp2 = onResult (.),然后写comp2 not (&&)定义一个 NAND 操作。

另一个概括——实际上包含上述内容——是定义将函数应用于更大值的组件的运算符。这里的一个例子是firstand secondin Control.Arrow,它适用于 2 元组。Conal Elliott 的语义编辑器组合器就是基于这种方法。

稍微不同的情况是,当您在某种类型上有一个多参数函数b和一个函数a -> b时,需要使用 将它们组合成一个多参数函数a。对于 2 元函数的常见情况,该模块Data.Function提供了on组合器,您可以使用它来编写表达式,例如compare `on` fst比较 2 元组的第一个元素。

当一个参数被多次使用时,这是一个更棘手的问题,但这里也可以提取有意义的重复模式。这里的一个常见情况是将多个函数应用于单个参数,然后使用另一个函数收集结果。这恰好对应Applicative于函数的实例,它让我们可以编写表达式(&&) <$> (> 3) <*> (< 9)来检查一个数字是否在给定的范围内。

重要的是,如果您想在实际代码中使用其中的任何内容,请考虑表达式的含义以及它如何反映在结构中。如果您这样做,然后使用有意义的组合器将其重构为无点样式,您通常会使代码的意图比其他方式更清晰,这与pointfree.

于 2011-12-30T17:26:25.857 回答
5

是的!技巧之一是用前缀而不是中缀来写你的点。然后你应该能够找到看起来像函数组合的新东西。这是一个例子:

banana2 f = appleTurnover . banana1 f
          = (.) appleTurnover (banana1 f)
          = ((.) appleTurnOver) . banana1 $ f
banana2 = (appleTurnover .) . banana1

pointfree 实用程序的源代码包含更多内容,但这个可以处理很多情况。

banana4 f x y = appleTurnover . banana3 f x y
              = (.) appleTurnover ((banana3 f x) y)
              = ((.) appleTurnover) . (banana3 f x) $ y
banana4 f x = ((.) appleTurnover) . (banana3 f x)
            = (.) ((.) appleTurnover) (banana3 f x)
            = ((.) ((.) appleTurnover)) ((banana3 f) x)
            = ((.) ((.) appleTurnover)) . (banana3 f) $ x
banana4 f = ((.) ((.) appleTurnover)) . (banana3 f)
          = (.) ((.) ((.) appleTurnover)) (banana3 f)
          = ((.) ((.) ((.) appleTurnover))) (banana3 f)
          = ((.) ((.) ((.) appleTurnover))) . banana3 $ f
banana4 = ((.) ((.) ((.) appleTurnover))) . banana3
        = (((appleTurnover .) .) .) . banana3
于 2011-12-30T16:33:26.310 回答
3

我使用以下术语重写系统:

\x -> f x ------> f 
f y x ----------> flip f x y
\x -> f (g x) --> f . g

它是不完整的(请阅读有关组合逻辑的书籍中的原因),但这已经足够了:

这是香蕉2:

banana2 f = appleTurnover . banana1 f

重写为 lambda:

banana2 = \f -> appleTurnover . banana1 f

以前缀样式写 (.):

banana2 = \f -> (.) appleTurnover (banana1 f)

注意

banana2 = \f -> ((.) appleTurnover) (banana1 f)

因此可以应用规则 3。f(.) appleTurnovergbanana

banana2 = ((.) appleTurnover) . banana1
于 2011-12-30T17:31:02.663 回答
2

有一个包采用 Haskell 函数定义并尝试以某种样式pointfree重新编写它。pointfree我建议尝试使用它来获得新的想法。有关详细信息,请参阅此页面;该软件包可在此处获得。

于 2011-12-30T17:02:30.107 回答
0

由于无点样式是组合子样式,只需应用已知的组合子定义,向后读取它们以进行替换:

B f g x = f (g x)     -- (.) , <$> for ((->) a)
C f x y = f y x       -- flip
K x y = x             -- const
I x = x               -- id
S f g x = f x (g x)   -- <*> , ap  for ((->) a)
W f x = f x x         -- join
(f >>= g) x = g (f x) x
(f =<< g) x = f (g x) x

有时liftMx, liftAx,可以简化事情sequencesequenceA我也会将foldr, unfoldr,iterateuntil视为基本组合器。

通常,使用运算符部分也有帮助:

op a b = (a `op` b) = (`op` b) a = (a `op`) b

一些模式可以变得熟悉,因此可以直接使用:

((f .) . g) x y = f (g x y)
((. f) . g) x y = g x (f y)

(((f .) .) . g) x y z = (f .) (g x y) z = f (g x y z)
(((. f) .) . g) x y z = (. f) (g x y) z = g x y (f z)

等等

于 2013-08-11T17:19:47.420 回答